Testing
This section provides some guidelines for testing your IHP applications.
Setup
Start by creating a new module for your tests (perhaps for an individual controller, something like test/Web/Controller/SomeControllerSpec.hs
), then import the IHP testing mock framework and Hspec (a Haskell testing library). You will also need to import your project’s Main
module (this is usually where a project’s InitControllerContext
instance is defined). Your imports will likely look something like this:
module ExampleSpec where
import Test.Hspec
import IHP.Test.Mocking
import IHP.FrameworkConfig (ConfigBuilder(..))
import IHP.Prelude
import IHP.QueryBuilder (fetch, query)
import Web.Types
import Web.Routes
import Generated.Types
import Main ()
Writing a test module
The IHP.Test.Mocking
module has functions to mock ModelContext
, RequestContext
and ApplicationContext
. These contexts are created with the mockContext
function, which requires a ConfigBuilder
and an IHP application as inputs. This function should be called before the tests start:
-- a function like this probably already exists in your Config module:
makeConfig :: IO ConfigBuilder
makeConfig = ...
spec :: Spec
spec = beforeAll (makeConfig >>= mockContext WebApplication) do
...
In order to execute database queries and run controller actions, the implicit context paramaters must be bound in the testing environment using withContext
. Here is an example test to check there are no users in the database:
describe "User controller" $ do
it "has no existing users" $ withContext do
users <- query @User |> fetch
users `shouldBe` []
The response of a controller action can be tested (but currently only the raw HTML content is returned):
it "responds with content" $ withContext do
content <- mockActionResponse NewUserAction
content `shouldBe` "<!DOCTYPE HTML>...</html>"
It is also possible to check the HTTP status of a controller response and response headers:
it "returns a redirect header" $ withContext do
hs <- headers (mockAction NewUserAction)
lookup "Location" hs `shouldNotBe` Nothing
it "creates a new user" $ withParams [("a-test-param","some-value")] do
mockActionStatus CreateUserAction `shouldReturn` status200
Runing a test
To execute a single test spec, load the test module into GHCI and run hspec spec
(make sure your database server is running).
Next steps
For more details on how to structure test suites see the Hspec manual (a Haskell testing library). You also might want to check out the cool Hedgehog library for automated property tests.
Complete example
The following is a complete example of the above code:
module ExampleSpec where
import Network.HTTP.Types.Status (status200)
import Test.Hspec
import IHP.Test.Controller.Mocking
import IHP.FrameworkConfig (ConfigBuilder(..))
import IHP.Prelude
import IHP.QueryBuilder (fetch, query)
import Web.Types
import Web.Routes
import Generated.Types
import Main ()
-- a function like this probably already exists in your Config module:
makeConfig :: IO ConfigBuilder
makeConfig = ...
spec :: Spec
spec = beforeAll (makeConfig >>= mockContext WebApplication) do
describe "User controller" $ do
it "has no existing users" $ withContext do
users <- query @User |> fetch
users `shouldBe` []
it "responds with some content" $ withContext do
content <- mockActionResponse NewUserAction
content `shouldBe` "<!DOCTYPE HTML>...</html>"
it "creates a new user" $ withParams [("a-test-param","some-value")] do
mockActionStatus CreateUserAction `shouldReturn` status200
it "returns a redirect header" $ withContext do
hs <- headers (mockAction NewUserAction)
lookup "Location" hs `shouldNotBe` Nothing