test-fixture: Test monadic side-effects

[ bsd3, library, test ] [ Propose Tags ]

Please see README.md

[Skip to Readme]




Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


  • No Candidates
Versions [RSS],,,,,,,,,,,
Change log CHANGELOG.md
Dependencies base (>=4.7 && <5), data-default-class, exceptions, haskell-src-exts, haskell-src-meta, mtl, template-haskell (>=2.10 && <2.13), th-orphans, transformers [details]
License BSD-3-Clause
Copyright 2016 CJ Affiliate by Conversant
Author Joe Vargas
Maintainer jvargas@cj.com
Category Test
Home page http://github.com/cjdev/test-fixture#readme
Source repo head: git clone https://github.com/cjdev/test-fixture
Uploaded by lexi_lambda at 2017-10-30T23:25:49Z
Reverse Dependencies 1 direct, 0 indirect [details]
Downloads 7861 total (22 in the last 30 days)
Rating 2.25 (votes: 2) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2017-10-30 [all 1 reports]

Readme for test-fixture-

[back to package description]

test-fixture Build Status

The test-fixture package is a Haskell library that makes it possible to easily write deterministic unit tests for code that encapsulates effects into monadic typeclasses. For example, given some typeclasses used to encapsulate effects:

class Monad m => MonadDB m where
  fetchRecord :: DBRecord a => Id a -> m (Either DBError a)
  insertRecord :: DBRecord a => a -> m (Either DBError (Id a))

class Monad m => MonadHTTP m where
  sendRequest :: HTTPRequest -> m (Either HTTPError HTTPResponse)

One can write IO instances to run the actual code in a real environment:

instance MonadDB IO where
  fetchRecord = Postgres.fetchRecord
  insertRecord = Postgres.insertRecord

instance MonadHTTP IO where
  sendRequest = sendRequestIO

Then use those typeclasses to implement some sort of side-effectful function:

sendAndFetch :: (MonadDB m, MonadHTTP m, DBRecord a)
             => HTTPRequest -> m (Either AppError a)
sendAndFetch = ...

Testing this function might be difficult because of all the different possible combinations of scenarios that must be considered. Creating lots of different monads and instances for each case can be boilerplate-heavy and tedious. Using test-fixture, the boilerplate is unnecessary:

mkFixture "Fixture" [ts| MonadDB, MonadHTTP |]

spec = describe "sendAndFetch" $ do
  it "returns a record when the http request and db fetch are successful" $ do
    let (fixture :: Monad m => Fixture m) = def
      { _fetchRecord = \_ -> return $ Right procureRecord
      , _sendRequest = \_ -> return $ Right responseOk
    let result = unTestFixture (sendAndFetch simpleRequest) fixture
    result `shouldBe` Right (User "someone@example.com")

  it "returns an error when the http request is not successful" $ do
    let (fixture :: Monad m => Fixture m) = def
      { _fetchRecord = \_ -> return $ Right procureRecord
      , _sendRequest = \_ -> return $ Left errorNotAuthorized
    let result = unTestFixture (sendAndFetch simpleRequest) fixture
    result `shouldBe` Left (AppHTTPError errorNotAuthorized)

For more information and a more complete explanation, see the documentation on Hackage.