How do I write integration tests for Yesod handlers that call out to other services?

90 views
Skip to first unread message

Jason Whittle

unread,
May 19, 2016, 12:24:30 PM5/19/16
to Yesod Web Framework
I’m using Yesod to write a REST API server that calls out to another REST API server as part of many of its handlers using http-conduit. I’ve been following the example set by the scaffolded tests in writing integration tests for my handlers, which works great if the only set-up I need to do for my test is alter the database state. 

How do I test Yesod handlers that make calls to an external API server without actually hitting the external server? To be clear, I can write the handlers so that they make the correct calls to the external API, I just haven’t figured out how to isolate that aspect in testing. I read Unit Testing IO in Haskell which sounds like what I’m looking for, except I want to make it work in yesod-test. 

Michael Snoyman

unread,
May 22, 2016, 12:08:36 AM5/22/16
to Yesod Web Framework
There are lots of ways to cut this, but ultimately it will come down to:

* Add a field to your App datatype indicating whether to do real calls or fake calls
* Set that appropriately from both the real applications and the test runner

You could either make it a boolean field and add fake-call logic throughout your application, or provide a function in your App data type for performing the calls and then mock it out. I'd probably lean towards the latter to centralize the mocking logic.

On Thu, May 19, 2016 at 7:24 PM Jason Whittle <jasonw...@gmail.com> wrote:
I’m using Yesod to write a REST API server that calls out to another REST API server as part of many of its handlers using http-conduit. I’ve been following the example set by the scaffolded tests in writing integration tests for my handlers, which works great if the only set-up I need to do for my test is alter the database state. 

How do I test Yesod handlers that make calls to an external API server without actually hitting the external server? To be clear, I can write the handlers so that they make the correct calls to the external API, I just haven’t figured out how to isolate that aspect in testing. I read Unit Testing IO in Haskell which sounds like what I’m looking for, except I want to make it work in yesod-test. 

--
You received this message because you are subscribed to the Google Groups "Yesod Web Framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to yesodweb+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jason Whittle

unread,
May 27, 2016, 4:15:13 PM5/27/16
to Yesod Web Framework, mic...@snoyman.com
Thank you, Michael, I appreciate the guidance. 

Is anyone aware of an example of this set-up in a public repository? 

Cheers, 
Jason Whittle

Jason Whittle

unread,
Aug 31, 2016, 7:01:06 PM8/31/16
to Yesod Web Framework, mic...@snoyman.com
Thank you, Michael, I followed your advice, adding a function to my App type, setting it to be the real function in makeFoundation, and then overriding it with a mock within each test. However, when I do this, the handler still uses the actual function, rather than the mock function provided for it. Where in the yesod-test hierarchy should I override the App so that the handler sees the mock? 

For more detail, I have written out the relevant code at http://stackoverflow.com/questions/39259340/how-do-i-modify-my-yesod-app-during-a-test

Thank you. 

Cheers, 
Jason Whittle

On Sunday, May 22, 2016 at 12:08:36 AM UTC-4, Michael Snoyman wrote:

Christopher Allen

unread,
Aug 31, 2016, 7:07:12 PM8/31/16
to yeso...@googlegroups.com, Michael Snoyman
One thing I would add is that it may be a good idea to make the mock
version of the function log that it was invoked, perhaps at a "debug"
loglevel in case you need to debug that later.

I do think function > boolean for this use-case. Debugging lattices of
boolean flags makes me twitchy.
--
Chris Allen
Currently working on http://haskellbook.com

Jason Whittle

unread,
Aug 31, 2016, 7:22:28 PM8/31/16
to Yesod Web Framework, mic...@snoyman.com
Thank you for the suggestion, Chris. If I can get this thing to work, I plan to make the pieces I’m creating publicly available, at which point I hope you’ll take a look at it. 

One issue with your suggestion is that logging in the mock is complicated by the fact that the function type that is part of App returns an IO action. I ended up doing this because I couldn’t figure out how to have the function in App return a Handler action. 

Cheers, 
Jason Whittle

Michael Snoyman

unread,
Sep 1, 2016, 6:58:04 AM9/1/16
to yeso...@googlegroups.com
IIUC, you just need to override the field at a different spot in the code. Using the scaffolded site as an example, I'd override here:


To unsubscribe from this group and stop receiving emails from it, send an email to yesodweb+unsubscribe@googlegroups.com.

Jason Whittle

unread,
Sep 1, 2016, 7:59:48 PM9/1/16
to Yesod Web Framework, mic...@snoyman.com
Thank you for the suggestion. I stuck the creation of an MVar in the location you suggested and made the mock function use the MVar. The problem I ran into then was that I couldn’t find a way to pass the MVar to the body of the spec. What I ended up doing was to create an alternative to Yesod.Test that does everything exactly the same, except that it also passes around a mock in a manner similar to how it passes around the site. 

Is this something that people would be interested in having added to the yesod-test package? 

Cheers, 
Jason

Jason Whittle

unread,
Sep 6, 2016, 4:16:05 PM9/6/16
to Yesod Web Framework, mic...@snoyman.com
For future readers: If you get to this point and want more information about how I addressed this issue, I answered my own question with further details at http://stackoverflow.com/questions/39259340/how-do-i-modify-my-yesod-app-during-a-test/39357087 and the modified code for Yesod.Test can be found at https://github.com/yesodweb/yesod/pull/1274

Cheers, 
Jason Whittle
Reply all
Reply to author
Forward
0 new messages