Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: Testing web APIs

I've been wondering what methods (if any) are used for testing Haskell applications that do requests over the network? Coming from Ruby land, I've been searching for anything that can either stub or "simulate" network calls for the purpose of testing Haskell functions. I'm particularly interested in a "VCR"-like solution (e.g.: https://github.com/myronmarston/vcr), as it seems to be the most viable option, IMHO.

So, I want to be able to record network request/response pairs once and then reuse these records for the subsequent tests. I have come up with my own simplistic concoction that starts a web server (warp) before the tests, serving prerecorded responses, but I have to point all URLs within the application to "localhost". I presume, this is not always possible to replace all URLs within the application. Although I'm quite happy with my own setup described above (and would like to make a dedicated testing tool/framework "plugin" from it later), but I'd rather not reinvent the wheel.

like image 708
Slava Kravchenko Avatar asked Sep 14 '12 12:09

Slava Kravchenko


1 Answers

Check out Control.Proxy.Tutorial. If you can write a Proxy wrapper around your type, then you can easily swap out the test interface and a real interface like this:

client <-< simulatedServer

client <-< realServer

Edit: To answer your question in the comment, you use a Server to write a wrapper around your simpleHTTP requests:

realServer
 :: HStream ty => Request ty -> Server (Request ty) (Result (Response ty)) IO r
realServer = foreverK $ \req -> do
    result <- lift $ simpleHTTP req
    respond result

The simulated server would look like:

simulatedServer
 :: (Monad m, HStream ty)
 => Request ty -> Server (Request ty) (Result (Response ty)) m r
simulatedServer = foreverK $ \req -> do
    result <- lift $ simulatedRequest req
    respond result

And your client would look like:

client
 :: (Monad m, HStream ty) => () -> Client (Request ty) (Result (Response ty)) m r
client () = forever $ do
    let req = <something>
    result <- request req
    lift $ doSomethingWith result

Then you can test the real server and fake server using:

-- Test the real server
main = runSession $ client <-< realServer

-- Test the fake server
main = runSession $ client <-< simulatedServer

The client and simulatedServer are polymorphic in the base monad only because I don't know what base monad they would use for your testing. The only requirement is that the two things you compose have the same base monad or that at least one is polymorphic in the base monad.

like image 190
Gabriella Gonzalez Avatar answered Oct 13 '22 19:10

Gabriella Gonzalez