Haskell: How to test a Spock app that uses wreq?

254 Views Asked by At

I've written a very simple app in Haskell using Spock and wreq. I want to write some tests, but I'm not sure how to do it.

All the app does is make an HTTP request to another server and translate the JSON response into a human-friendly message. Here is the code if it helps. I want to write a test that asserts the human-friendly message is correct given a certain JSON response.

In Ruby, I would mock out the HTTP client, in this case wreq, to control the JSON response I get, but I'm not sure how, or even if, to do that in Haskell.

From what I've gathered out of my research, it sounds like type-class-constrained type variables are the way to go, which looks like dependency injection to me, but I'm unsure how to do this within the context of Spock and wreq.

The Spock testing tutorial describes how to test Spock at the IO Application level which sounds great. The part I'm unsure about is how to "inject" a mock HTTP client to control the JSON response.

Any help is appreciated!

1

There are 1 best solutions below

1
On

If we want to support different methods of making request, we must abstract over them. You could make your handlers accept as argument a function that performs HTTP requests, and then pass different functions for testing and production. If you have more than one related function, put them in a record (the handle pattern).


If we are using cabal-install > 2.0 (with the new-* commands) another possible option is to use a module signature to switch implementations between the test suite and the final executable app. This solution also makes heavy use of the internal convenience libraries feature of Cabal.

The basic idea is this: we put our Spock application in its own library, but do not make it depend on wreq directly. Instead, we declare in the same library a signature Requests.hsig like this:

signature SomeSpockApp.Requests where

import           Data.Aeson (FromJSON)

data Token

doGET :: FromJSON a => String -> Token -> IO a 

It defines a high-level interface for performing HTTP requests. The code in the library will import this signature. For the rest of the code in the library, SomeSpockApp.Requests is just another module.

Next, we define a convenience library that will provide an actual module SomeSpockApp.Requests (same name as the signature, except that now it is a hs file). It will contain the "mock" code. Of course the definition of doGET must be compatible with the signature.

We also define another convenience library with another SomeSpockApp.Requests module. This one should depend on wreq and fulfill the methods of our signature using wreq's functions.

In the test suite, we should depend on both our Spock app library and the mock library. The names of the signature and of the mock implementation module line up perfectly, so nothing more needs to be done. (If the names dont' match, we can use a mixins stanza in the cabal file to rename the module).

In the app executable, we should depend on both our Spock app library and the wreq-using library.