yesod-test-0.3.1: integration testing for WAI/Yesod Applications

Safe HaskellNone




Yesod.Test is a pragmatic framework for testing web applications built using wai and persistent.

By pragmatic I may also mean dirty. It's main goal is to encourage integration and system testing of web applications by making everything easy to test.

Your tests are like browser sessions that keep track of cookies and the last visited page. You can perform assertions on the content of HTML responses, using css selectors to explore the document more easily.

You can also easily build requests using forms present in the current page. This is very useful for testing web applications built in yesod for example, were your forms may have field names generated by the framework or a randomly generated _nonce field.

Your database is also directly available so you can use runDBRunner to set up backend pre-conditions, or to assert that your session is having the desired effect.


Declaring and running your test suite

runTests :: Application -> Pool conn -> SpecsConn conn -> IO ()Source

Runs your test suite, using you wai Application and ConnectionPool for performing the database queries in your tests.

You application may already have your connection pool but you need to pass another one separately here.

Look at the examples directory on this package to get an idea of the (small) amount of boilerplate code you'll need to write before calling this.

describe :: String -> SpecsConn conn -> SpecsConn connSource

Start describing a Tests suite keeping cookies and a reference to the tested Application and ConnectionPool

it :: String -> OneSpec conn () -> SpecsConn connSource

Describe a single test that keeps cookies, and a reference to the last response.

type SpecsConn conn = StateT (SpecsData conn) IO ()Source

The specs state monad is where describe runs. parameterized by a database connection. You should create type Specs = SpecsConn MyDBConnection

type OneSpec conn = StateT (OneSpecData conn) IOSource

The OneSpec state monad is where it runs.

Making requests

To make a request you need to point to an url and pass in some parameters.

To build your parameters you will use the RequestBuilder monad that lets you add values, add files, lookup fields by label and find the current nonce value and add it to your request too.

post :: ByteString -> RequestBuilder () -> OneSpec conn ()Source

Perform a POST request to url, using params

post_ :: ByteString -> OneSpec conn ()Source

Perform a POST request without params

get :: ByteString -> RequestBuilder () -> OneSpec conn ()Source

Perform a GET request to url, using params

get_ :: ByteString -> OneSpec conn ()Source

Perform a GET request without params

doRequest :: Method -> ByteString -> RequestBuilder a -> OneSpec conn ()Source

General interface to performing requests, letting you specify the request method and extra headers.

byName :: Text -> Text -> RequestBuilder ()Source

Add a parameter with the given name and value.

fileByName :: Text -> FilePath -> Text -> RequestBuilder ()Source

Add a file to be posted with the current request

Adding a file will automatically change your request content-type to be multipart/form-data

Yesod cat auto generate field ids, so you are never sure what the argument name should be for each one of your args when constructing your requests. What you do know is the label of the field. These functions let you add parameters to your request based on currently displayed label names.

byLabel :: Text -> Text -> RequestBuilder ()Source

fileByLabel :: Text -> FilePath -> Text -> RequestBuilder ()Source

Does the current form have a _nonce? Use any of these to add it to your request parameters.

addNonce :: RequestBuilder ()Source

For responses that display a single form, just lookup the only nonce available.

addNonce_ :: Query -> RequestBuilder ()Source

Lookup a _nonce form field and add it's value to the params. Receives a CSS selector that should resolve to the form element containing the nonce.

Running database queries

runDBRunner :: (MonadBaseControl IO m, MonadIO m) => (poolrunner m a -> Pool conn -> IO a) -> poolrunner m a -> OneSpec conn aSource

Run a persistent db query. For asserting on the results of performed actions or setting up pre-conditions. At the moment this part is still very raw.

It is intended that you parametize the first argument of this function for your backend runDB = runDBRunnder SqlPersist


assertEqual :: Eq a => String -> a -> a -> OneSpec conn ()Source

Asserts that the two given values are equal.

assertHeader :: HoldsResponse a => CI ByteString -> ByteString -> StateT a IO ()Source

Assert the given header key/value pair was returned.

assertNoHeader :: HoldsResponse a => CI ByteString -> StateT a IO ()Source

Assert the given header was not included in the response.

statusIs :: HoldsResponse a => Int -> StateT a IO ()Source

Assert the last response status is as expected.

bodyEquals :: HoldsResponse a => String -> StateT a IO ()Source

Assert the last response is exactly equal to the given text. This is useful for testing API responses.

bodyContains :: HoldsResponse a => String -> StateT a IO ()Source

Assert the last response has the given text. The check is performed using the response body in full text form.

htmlAllContain :: HoldsResponse a => Query -> String -> StateT a IO ()Source

Queries the html using a css selector, and all matched elements must contain the given string.

htmlCount :: HoldsResponse a => Query -> Int -> StateT a IO ()Source

Performs a css query on the last response and asserts the matched elements are as many as expected.

Utils for debugging tests

printBody :: HoldsResponse a => StateT a IO ()Source

Outputs the last response body to stderr (So it doesn't get captured by HSpec)

printMatches :: HoldsResponse a => Query -> StateT a IO ()Source

Performs a CSS query and print the matches to stderr.

Utils for building your own assertions

Please consider generalizing and contributing the assertions you write.

htmlQuery :: HoldsResponse a => Query -> StateT a IO [Html]Source

Query the last response using css selectors, returns a list of matched fragments

parseHTML :: Html -> CursorSource

Use HXT to parse a value from an html tag. Check for usage examples in this module's source.

withResponse :: HoldsResponse a => (SResponse -> StateT a IO b) -> StateT a IO bSource