Mocking API Requests in Node tests
Recently I sat down with my Pulldown Project, aiming to rewrite the tests. The problem with them was that they were network dependent. Each test would hit the real API and download the real file. This was not good for a number of reasons:
- I couldn't run the tests without an internet connection
- the tests were slow
- the tests were unreliable, they would sometimes pass, and other times not
Unreliable tests are worse than no tests, so I ripped them out and started again.
Meet Nock
The solution to this is Nock, a Node module for mocking HTTP requests. With Nock you can mock a HTTP request and make it always return a specific result. Here's an example:
var nock = require("nock"); var http = require("http");
var api = nock("http://javascriptplayground.com") .get("/test/") .reply(200, "Hello World");
http.get("http://javascriptplayground.com/test/", function(resp) { var str = ""; resp.on("data", function(data) { str += data; }); resp.on("end", function() { console.log("Got Result: ", str); }); });
In that code we do two things. First, we mock a request to http://javascriptplayground.com/test/
and make it return the string "Hello World" with a 200 status code. Then we use Node's http library to make a request and log it out. We then get "Got Result: Hello World" outputted when we run the above.
What's so great about this is that http.get
is none-the-wiser about what just happened. You don't have to change any code to make this work, just mock the request.
There's no requirement to return a string, either. You can return an object, an array, whatever you'd like.
A Gotcha
When you mock something using nock, it only works once. Once a URL you've mocked is hit, the mock is then destroyed. To fix this, you can make a specific mocked URL persist:
var api = nock("http://javascriptplayground.com") .persist() .get("/test/") .reply(200, "Hello World");
Now it will last forever, until you call cleanUp
, which I'll cover shortly.
Asserting
If you need to test thaat a specific URL is called, you can mock that URL and then call isDone()
to see if it got called:
var api = nock("http://javascriptplayground.com") .get("/test/") .reply(200, "Hello World");
// http.get code here api.isDone(); // => true
Clean Up
When you have lots of tests that do this, it's important to make sure they tidy up after themselves. The best way I've found of doing this is calling nock.cleanAll()
after each test. cleanAll()
removes all mocks completely. If you were using something like Mocha to do your tests, you might like to do this in the afterEach
method.
Further Reading
The best place to start is the nock README. There's a huge amount of documentation and a lot more nock can do that I've not covered.
If you'd like to see a real project that uses nock, we use it extensively in the Pulldown tests.
If you've ever used an alternative to Nock, or use other tools with it that you think I should mention here, please leave a comment.