After this lesson, students will be able to:
- Describe the importance of testing your code programmatically
- Use describe and assertion functions to do basic testing
Before this lesson, students should already be able to:
- Describe TDD
- Explain basic file and folder structure in a simple RESTful node app
We've now created several Express applications. All these apps cover a single topic, so most of the time, they are quite small. But when you create a larger application, the codebase will become bigger and more complex every time you add some features. At some point, adding code in file A will break features in file B, and to avoid these "side-effects", we need to test our app.
To do so in Node, we will use two libraries: one to run the tests and a second one to run the assertions.
Mocha will be our testing framework. From the mocha Website:
"Mocha is a feature-rich JavaScript test framework running on Node.js and the browser, making asynchronous testing simple and fun. Mocha tests run serially, allowing for flexible and accurate reporting, while mapping uncaught exceptions to the correct test cases."
For assertions, we will use Chai. From the Chai website:
"Chai is a BDD / TDD assertion library for node and the browser that can be delightfully paired with any javascript testing framework."
To be able to make HTTP requests inside tests, we will use a framework called supertest
.
Take the starter code provided - this is an app we've created when we talked about routing in Express.
To test this app, we need to install a couple of dependencies.
First, let's install mocha, and we will install this package globally using -g
:
npm install -g mocha
Then we will install chai using --save-dev
npm install chai --save-dev
Last dependency we need to install is supertest:
npm install supertest --save-dev
Now that we're configured, let's set up our file and folder structure. All the tests will be written inside a folder test
at the root of the app:
mkdir tests
Then we will write the tests inside a file called candies_tests.js
:
touch tests/candies_tests.js
Note: Explain that because our tests will request the application through HTTP, students have to make sure they are running the app while running the tests
Open the file candies_tests.js
. We now need to require some dependencies at the top of this file:
var should = require("chai").should(),
expect = require("chai").expect,
supertest = require("supertest"),
api = supertest("http://localhost:3000")
Make sure you set the url correctly, as this will be used to request the app and analyze the response.
All the tests need to be inside a describe
function. We will use one describe block per route:
describe("GET /candies", function(){
//tests will be written inside this function
});
First, we will write a test to make sure that a request to the index path /candies
returns a http status 200:
describe("Candies", function(done){
it("should return a 200 response", function(){
api.get("/candies")
.set("Accept", "application/json")
.expect(200, done)
})
})
Now go in the command line and type mocha
, you should get an output looking like :
This test is passing!
Every block of code that starts with it()
represents a test.
The callback
represents a function that Mocha will pass to the code so that the next test will be executed only when the current is finished and the done
function is called - this allows tests to be executed once at a time.
Now, let's verify the content of the response by looking at the data sent back by hitting the /candies
endpoint:
[{
"id": 1,
"name": "Chewing Gum",
"color": "Red"
}, {
"id": 2,
"name": "Pez",
"color": "Green"
}, {
"id": 3,
"name": "Marshmallow",
"color": "Pink"
}, {
"id": 4,
"name": "Candy Stick",
"color": "Blue"
}]
We can write a test that verifies the response is an array:
it("should return an array", function(done){
api.get("/candies")
.set("Accept", "application/json")
.end(function(error, response){
expect(response.body).to.be.an('array');
done()
})
})
We can write another test that verifies the presence of a field in the response:
it("should return an object that have a field called 'name' ", function(done){
api.get("/candies")
.set("Accept", "application/json")
.end(function(error, response){
expect(response.body[0]).to.have.property('name');
done()
})
})
We can also send data to the server and test the behavior - in our case, we want to make sure that when we post some JSON to /candies
, a new object is added to the array candies.
Because we are going to test another route, lets add another describe block:
describe("POST /candies", function(){
})
For this test, we need to:
- Create a new object by sending a
POST
request - Verify that a new object has been "saved" by requesting the index route
For this, we will use before
blocks. A before
block will be executed for every it
function is called inside a describe
block.
Add this inside the new describe
block:
before(function(done){
api.post("/candies")
.set("Accept", "application/json")
.send({
"id": 5,
"name": "Lollipop",
"color": "Red"
}).end(done)
})
This code will be called for every test we will add into the current describe
block.
Now, we can verify that calling "POST" will add an object to candies:
it("should add a candy object to the collection candies and return it", function(done){
api.get("/candies")
.set("Accept", "application/json")
.end(function(error, response){
expect(response.body.length).to.equal(5);
done()
})
})
Run the mocha
command in the terminal, you should now have four passing tests!
Note: This can be a pair programming activity or done independently.
Add tests to the suite:
- Write a test that make sure the object returned when you call show with a specific ID contains the right fields.
- Write a test that ensure an object is deleted from the array candies when you call delete.
- Write a test that ensure a property is updated when you call
PUT /candies/:id
Review the answers to the tests specs above
We've covered the principles of testing in JavaScript, but Chai offers a lot of different expectations syntaxes. Check the Chai Documentation
- How does Mocha work with Chai to write tests in your JavaScript application?
- Describe how to configure your app to use Mocha and Chai.