In the previous posts, we created a Node.js web application and we learned how to manage its deployment, development and debugging with Docker. Although in the early stages of development it seems that manual testing is enough to check that our app works properly, the growth of its codebase and complexity will make this solution very inefficient and we will need automatic, fast and widespread tests to check the behavior of app's components.
It's easy to create a testing environment and it doesn't need particular configurations as regards the Docker container. Meanwhile, on the app side, we can use the stack that we want. In this guide, we'll use Mocha as test framework and Chai as assertion library.
Prerequisites
- The
Dockerfile
,index.js
andpackage.json
files from the previous post.
Installing Mocha and Chai
Before building a new image, we have to update the package.json
file and add mocha
, chai
and chai-http
(a Chai plugin for testing HTTP endpoints) to the dependencies. We can do it in several ways:
Adding the dependencies to the file manually.
Starting a container, update the file inside the container and propagate changes to the outside by using a volume (we saw how to do in the second article of this series).
Starting a container, update the file inside the container and copy it to the outside in our working directory.
Since the first option doesn't need further clarification and the second one has already been examined, let's take the opportunity to see a new approach.
If you haven't already created an image from the Dockerfile, do it now
$ docker build --tag my-app .
Run a container from the image and edit the entrypoint to launch a shell in it (we already discussed on this practice in the post about the debugging).
$ docker run -it --entrypoint /bin/sh my-app
Install the needed dependencies with NPM
$ npm install -D mocha chai chai-http
When the installation it's over, exit from the shell (you can do with CTRL + D
or exit
command) and copy or overwrite the package.json
file from inside the container to the outside, in our working directory.
$ docker cp <CONTAINER_ID>:app/package.json ./package.json
Writing the tests
We'll create a test to verify that the application shows the 'Hello world' message on the homepage.
But first, we have to make a change in the index.js
var express = require('express')
var app = express()
var port = process.env.PORT || 3000
app.get('/', function (req, res) {
res.send('Hello World!')
})
if (process.env.NODE_ENV === 'test') {
module.exports = app
} else {
app.listen(port)
}
We added an if block to check, by using the NODE_ENV
environment variable, if the code is executed in a testing context and if so, the app
object will be exported (we'll see why shortly) otherwise the application will be started normally.
Let's move on to the test. Since Mocha runs all tests inside the test
folder by default, create in your working directory the following test/homepage.js
file
// Import the dependencies
var chai = require('chai')
var chaiHttp = require('chai-http')
// Import the application to test
var app = require('../index')
// Configure Chai
chai.use(chaiHttp)
chai.should()
describe('Homepage', () => {
it('should show the Hello World message', done => {
chai
.request(app)
.get('/')
.end((error, response) => {
response.text.should.equal('Hello World!')
done()
})
})
})
Let's see more in detail how this test is structured:
We use
chai.use()
to add thechai-http
plugin to Chai.We use
chai.should()
to choose should as assertion style. Other styles are assert and expect.We use
it()
to create a test,describe()
to group them in suites.With
request()
we set an Express application of which we want test the endpoints with Chai.In Mocha, we use
done()
at the end of a test to manage the assertions rightly in asynchronous functions likeend()
.
Running the tests
Let's add a test
script to the package.json
file to launch Mocha (note that here we pass the NODE_ENV
environment variable used in index.js
)
{
"name": "nodejs-app",
"dependencies": {
"express": "^4.17.0"
},
"scripts": {
"start": "node index.js",
"dev": "npx nodemon index.js",
"debug": "node --inspect=0.0.0.0:9229 index.js",
"test": "NODE_ENV=test npx mocha"
},
"devDependencies": {
"chai": "^4.2.0",
"chai-http": "^4.3.0",
"mocha": "^6.1.4",
"nodemon": "^1.19.1"
}
}
Build a new image of our app and run a container
$ docker build --tag my-app .
$ docker run -d -p 4000:3000 my-app
At this point we can execute the tests in two ways:
- we can run them like a process outside the container (this choice is made, for example, when we need to run them in a CI/CD pipeline)
$ docker exec <CONTAINER_ID> npm test
- we can run them like a process inside the container by opening a shell in it
$ docker exec -it <CONTAINER_ID> /bin/sh
# npm test
Conclusions
Now we learned how to manage easily every phase of Node.js application development by using Docker. In the next post, we'll talk about services integration and we'll see how to include a database in our application by using a new tool, Docker Compose.