Integration testing is a critical part of any QA effort. It can be used as a final check before any backend API deployment and give confidence to the development team that the deploy is safe.
We’ll work through a simple API test written in NodeJS and use a couple of popular testing frameworks:
chai-http - extensions for making HTTP requests to APIs
For our example we’ll use an existing public API - the Yahoo Weather API. We’ll build two tests, one positive and one negative.
Create the project
Let’s get the project started. Assuming you already have npm installed, in a new directory, initialize the project and install the relevant npm modules:
1
2
3
$ mkdir sample-yahoo-api-tests
$ npm init
$ npm install --save-dev mocha chai chai-http
This creates a package.json file that contains the package dependencies mentioned above. Let’s make a few changes to this as so (don’t change the versiopn numbers after each package in the devDependencies section as they will probably be different in your case):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"name": "sample-yahoo-api-tests",
"version": "1.0.0",
"description": "Yahoo Weather API tests",
"main": "index.js",
"dependencies": {
},
"devDependencies": {
"chai": "^3.5.0",
"chai-http": "^3.0.0",
"mocha": "^3.1.2"
},
"author": "",
"license": "ISC"
}
Mocha gives us a standard and simple format of writing tests. Generally we group tests into individual test files. Each test file then has a smaller group and then the tests within.
Each file has a describe…it structure as so:
1
2
3
describe("Some feature or API")
it("Should or should not do something")
it("Should or should not do something else")
Our general structure will be:
1
2
3
4
5
6
7
8
9
describe('Yahoo Weather API', function() {
it("returns a result for Santa Monica, CA", function() {
// Test code here
});
it("returns null results for invalid location", function() {
// Test code here
});
});
chai-http allows us to easily make requests to an HTTP endpoint and handle the response. The general format is:
1
2
3
4
5
chai.request(server)
.get(path)
.then(function(res) {
// Test code here
});
The Yahoo endpoint we’re going to test is https://query.yahooapis.com/v1/public/yql?q=select%20item.condition%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22Santa%20Monica%2C%20CA%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys which will return us basic weather information for Santa Monica, CA:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"query": {
"count": 1,
"created": "2016-11-03T05:18:01Z",
"lang": "en-US",
"results": {
"channel": {
"item": {
"condition": {
"code": "31",
"date": "Wed, 02 Nov 2016 09:00 PM PDT",
"temp": "70",
"text": "Clear"
}
}
}
}
}
}
Building the first (positive) test
Putting this all together, here’s our first test that we’ll create in test/yahoo-weather.spec.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var chai = require('chai');
var chaiHttp = require('chai-http');
var expect = chai.expect;
// Tell chai to use chai-http
chai.use(chaiHttp);
describe('Yahoo Weather API', function() {
it("returns a result for Santa Monica, CA", function() {
Note that there are actually 4 assertion tests here:
1
expect(res).to.have.status(200);
This ensures that the HTTP response code for this request is 200, which is a general success response. If the server returns some error, we’ll get a failure in our tests here.
As you can see, we now have a simple working API test! Let’s go ahead and add our second test that ensure we get a null response for a city that doesn’t exist:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var chai = require('chai');
var chaiHttp = require('chai-http');
var expect = chai.expect;
// Tell chai to use chai-http
chai.use(chaiHttp);
describe('Yahoo Weather API', function() {
it("returns a result for Santa Monica, CA", function() {
Our second test is similar to the first, except we’re passing in a city name that we know doesn’t exist (‘AAAAAAAAAAAA’) and expecting the results to be null. Let’s run this and make sure it works:
✓ returns null results for invalid location (207ms)
2 passing (488ms)
Tidy up
Let’s do some tidying up. Firstly, typing in the full test command is long-winded. We’ll create a script in package.json to make it easier to run. Add the scripts section into your package.json file:
Next, let’s clear up our code a little. We’ll add a helper file that’ll clean up the API URL that we’re calling for both tests. Create test/yahoo-helpers.js and add this as content:
In October, Facebook released the first version of their Yarn package manager for Javascript.
Yarn is a group effort between Facebook, Google, Exponent and Tilde to solve several issues they have faced with the standard npm client around speed and security. The result is a super-fast drop-in replacement for npm that will dramatically speed up install times.
Whereas npm pulls modules from the source every time you run an install, Yarn keeps a local cache (outside of your project directory) and copies from that. This means it’s not only far quicker than npm but also works if the machine has no internet connection (for example a secure build server),
The clean npm install time for the Whipclip web app is typically around 3 minutes. With yarn, it takes 17 seconds. That’s a huge improvement not just for local dev but also for our CI workflow. Our unit test task has dropped from 2 minutes 13 seconds to 50 seconds.
1
2
3
4
5
6
7
$ rm -rf node_modules
$ time npm install
real 3m3.092s
$ rm -rf node_modules
$ time yarn
real 0m16.805s
The beauty of Yarn is that it’s a drop-in replacement for npm and uses your existing package.json file. You don’t need to change your workflow or tech; simply run yarn instead of npm install and you’re all set!