Automated Testing

Thanks to Haskell’s type system we can avoid most the errors that we might encounter when using less safe languages, but some automated tests can still help verify our code is correct. Let’s explore how we can write unit tests for our HTTP service built with Spock.

Hello world

{-# LANGUAGE OverloadedStrings #-}
module Main where

import Web.Spock
import Web.Spock.Config

main :: IO ()
main =
    do spockCfg <- defaultSpockCfg () PCNoDatabase ()
       runSpock 8080 (spock spockCfg app)

app :: SpockM () () () ()
app =
    do get root $
           text "Hello World!"
       get ("hello" <//> var) $ \name ->
           do text ("Hello " <> name)

Here is a simple Spock application, similar to the one from the Hello World. It has two routes, / which responds with “Hello, world!”, and /hello/:name, which responds with “Hello $NAME”, where name is the value of the second path segment.

It type checks, so we are confident that the application will run without crashing, but we might want some tests to check that the business logic has been correctly implemented. We can do this with a little help from the Hspec and Hspec-Wai libraries.

Add Hspec and Hspec-Wai to your tests dependencies in your project’s Cabal file in the test-suite section:

-- snip
test-suite app-test
  build-depends:   Spock >=0.14
                 , base >=4.7 && <5
                 , hspec
                 , hspec-wai
-- snip

Creating tests

Now we create a file test/Spec.hs, start with the test setup and add a basic test.

module Spec where

import Test.Hspec

main :: IO ()
main = hspec spec

spec :: Spec
spec =
  describe "the universe" $
  it "behaves the way we expect it to" $ do
    1 `shouldBe` 1

We can run this with stack test, and providing mathematics hasn’t changed since last time we used it we should see something like this printed to the console:

Spec
  the universe
    behaves the way we expect it to

Finished in 0.0050 seconds
1 examples, 0 failures

Now that the tests run we can start testing the web application using Hspec-Wai. This library tests our application as an IO Application, where Application is defined in Network.Wai. We’ll need to restructure our application slightly to expose this to the tests.

{-# LANGUAGE OverloadedStrings #-}
module Main (main, app) where

import Web.Spock
import Web.Spock.Config
import Network.Wai (Middleware)

main :: IO ()
main =
    runSpock 8080 app

app :: IO Middleware
app =
    do spockCfg <- defaultSpockCfg () PCNoDatabase ()
       spock spockCfg app

routes :: SpockM () () () ()
routes =
    do get root $
           text "Hello World!"
       get ("hello" <//> var) $ \name ->
           do text ("Hello " <> name)

app has been renamed to routes, and the main function has been split into two. The new main is responsible for only the running of the application, with the new app function being responsible for configuring the application. Through the new app we can access an IO Middleware which we can convert into an IO Application in the tests using spockAsApp.

{-# LANGUAGE OverloadedStrings #-}
module Spec where

import Main (app)
import Test.Hspec
import Test.Hspec.Wai
import Web.Spock (spockAsApp)

main :: IO ()
main = hspec spec

spec :: Spec
spec =
    with (spockAsApp app) $
        do describe "GET /" $
               do it "serves the home page" $
                      get "/" `shouldRespondWith` "Hello World" {matchStatus = 200}
           describe "GET /hello/:name" $
               do it "returns hello to spock" $
                      get "/hello/spock" `shouldRespondWith` "Hello spock"
                  it "returns hello to uhura" $
                      get "/hello/uhura" `shouldRespondWith` "Hello uhura"

The shouldRespondWith function is used to make assertions about the status code, headers and body content of the HTTP response from the Spock application. get "/hello/spock" shouldRespondWith "Hello spock" asserts that a GET request to /hello/spock will result in a HTTP response with the body content “Hello spock”.

For more information on testing check out Hspec-Wai’s documentation on Hackage.