Reduce inconsistencies with previous step

Adapt instructions so that the process follows more naturally from the previous step.
This commit is contained in:
Matthew McClure 2021-02-01 15:12:41 -05:00 committed by GitHub
parent 1be59f2fcc
commit f7a25beac4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 142 additions and 99 deletions

View File

@ -6,22 +6,48 @@ description: How to Build and Run your Tests using Node.js and Mocha frameworks
{% include_relative nav.html selected="4" %}
Testing is an essential part of modern software development. Testing can mean a lot of things to different development teams. There are unit tests, integration tests and end-to-end testing. In this guide we take a look at running your unit tests in Docker. Let's assume we have defined Mocha tests in a `./test` folder within our application.
## Prerequisites
Work through the steps to build an image and run it as a containerized application in [Use your container for development](develop.md).
## Introduction
Testing is an essential part of modern software development. Testing can mean a lot of things to different development teams. There are unit tests, integration tests and end-to-end testing. In this guide we take a look at running your unit tests in Docker.
## Create a test
Let's define a Mocha test in a `./test` directory within our application.
```shell
$ mkdir -p test
```
Save the following code in `./test/test.js`.
```javascript
var assert = require('assert');
describe('Array', function() {
describe('#indexOf()', function() {
it('should return -1 when the value is not present', function() {
assert.equal([1, 2, 3].indexOf(4), -1);
});
});
});
```
### Running locally and testing the application
Now that we have our sample application locally, lets build our Docker image and make sure everything is running properly. Run the following commands to build and then run your Docker image in a container.
Lets build our Docker image and confirm everything is running properly. Run the following command to build and run your Docker image in a container.
```shell
$ docker build -t node-docker .
$ docker run -it --rm --name app -p 8080:80 node-docker
$ docker-compose -f docker-compose.dev.yml up --build
```
Now lets test our application by POSTing a JSON payload and then make an HTTP GET request to make sure our JSON was saved correctly.
```shell
$ curl --request POST \
--url http://localhost:8080/services/test \
--url http://localhost:8000/test \
--header 'content-type: application/json' \
--data '{
"msg": "testing"
@ -31,7 +57,7 @@ $ curl --request POST \
Now, perform a GET request to the same endpoint to make sure our JSON payload was saved and retrieved correctly. The “id” and “createDate” will be different for you.
```shell
$ curl http://localhost:8080/services/test
$ curl http://localhost:8000/test
{"code":"success","payload":[{"msg":"testing","id":"e88acedb-203d-4a7d-8269-1df6c1377512","createDate":"2020-10-11T23:21:16.378Z"}]}
```
@ -44,16 +70,16 @@ Run the following command to install Mocha and add it to the developer dependenc
$ npm install --save-dev mocha
```
## Refactor Dockerfile to run tests
## Update package.json and Dockerfile to run tests
Okay, now that we know our application is running properly, lets try and run our tests inside of the container. Well use the same docker run command we used above but this time, well override the CMD that is inside of our container with npm run test. This will invoke the command that is in the package.json file under the “script” section. See below.
```shell
```javascript
{
...
"scripts": {
"test": "mocha ./**/*.js",
"start": "nodemon server.js"
"start": "nodemon --inspect=0.0.0.0:9229 server.js"
},
...
}
@ -62,36 +88,26 @@ Okay, now that we know our application is running properly, lets try and run
Below is the Docker command to start the container and run tests:
```shell
$ docker run -it --rm --name app -p 8080:80 node-docker npm run test
> node-docker@0.1.0 test /code
$ docker-compose -f docker-compose.dev.yml run notes npm run test
Creating node-docker_notes_run ...
> node-docker@1.0.0 test /code
> mocha ./**/*.js
sh: 1: mocha: not found
Array
#indexOf()
✓ should return -1 when the value is not present
1 passing (11ms)
```
As you can see, we received an error. This error is telling us that the Mocha executable could not be found. Lets take a look at the Dockerfile.
```dockerfile
FROM node:14.15.4
WORKDIR /code
COPY package.json package.json
COPY package-lock.json package-lock.json
RUN npm ci --production
COPY . .
CMD [ "node", "server.js" ]
```
The error is occurring because we are passing the `--production` flag to the npm ci command when it installs our dependencies. This tells npm to not install packages that are located under the "devDependencies" section in the package.json file. Therefore, Mocha will not be installed inside the image and will not be found when we try to run it.
Since we want to follow best practices and not include anything inside the container that we do not need to run our application we cant just remove the `--production` flag. We have a couple of options to fix this. One option is to create a second Dockerfile that would only be used to run tests. This has a couple of problems. The primary being keeping two Dockerfiles up-to-date. The second option is to use multi-stage builds. We can create a stage for production and one for testing. This is our preferred solution.
### Multi-stage Dockerfile for testing
Below is a multi-stage Dockerfile tha we will use to build our production image and our test image. Add the highlighted lines to your Dockerfile.
In addition to running the tests on command, we can run them when we build our image, using a multi-stage Dockerfile. The following Dockerfile will run our tests and build our production image.
```dockerfile
FROM node:14.15.4 as base
@ -112,34 +128,35 @@ COPY . .
CMD [ "node", "server.js" ]
```
We first add a label to the `FROM node:14.15.4` statement. This allows us to refer to this build stage in other build stages. Next we add a new build stage labeled test. We will use this stage for running our tests.
We first add a label `as base` to the `FROM node:14.15.4` statement. This allows us to refer to this build stage in other build stages. Next we add a new build stage labeled test. We will use this stage for running our tests.
Now lets rebuild our image and run our tests. We will run the same docker build command as above but this time we will add the `--target test` flag so that we specifically run the test build stage.
```shell
$ docker build -t node-docker --target test .
[+] Building 0.7s (11/11) FINISHED
Sending build context to Docker daemon 22.35MB
...
=> => writing image sha256:049b37303e3355f...9b8a954f
=> => naming to docker.io/library/node-docker
Successfully built fa583b97c4dd
Successfully tagged node-docker:latest
```
Now that our test image is built, we can run it in a container and see if our tests pass.
```shell
$ docker run -it --rm --name app -p 8080:80 node-docker
> node-docker@0.1.0 test /code
docker run -it --rm -p 8000:8000 node-docker
> node-docker@1.0.0 test /code
> mocha ./**/*.js
Calculator
adding
✓ should return 4 when adding 2 + 2
✓ should return 0 when adding zeros
subtracting
✓ should return 4 when subtracting 4 from 8
✓ should return 0 when subtracting 8 from 8
4 passing (11ms)
Array
#indexOf()
✓ should return -1 when the value is not present
1 passing (12ms)
```
Ive truncated the build output but you can see that the Mocha test runner completed and all our tests passed.
@ -170,75 +187,101 @@ CMD [ "node", "server.js" ]
Now to run our tests, we just need to run the docker build command as above.
```dockerfile
$ docker build -t node-docker --target test .
[+] Building 1.2s (13/13) FINISHED
docker build -t node-docker --target test .
Sending build context to Docker daemon 22.35MB
Step 1/8 : FROM node:14.15.4 as base
---> f5be1883c8e0
...
=> CACHED [base 2/4] WORKDIR /code
=> CACHED [base 3/4] COPY package.json package.json
=> CACHED [base 4/4] COPY package-lock.json package-lock.json
=> CACHED [test 1/3] RUN npm ci
=> CACHED [test 2/3] COPY . .
=> CACHED [test 3/3] RUN npm run test
=> exporting to image
=> => exporting layers
=> => writing image sha256:bcedeeb7d9dd13d...18ca0a05034ed4dd4
Step 6/8 : RUN npm ci
---> Using cache
---> bcc5cd4a6a1e
Step 7/8 : COPY . .
---> 1528ebcb73fa
Step 8/8 : RUN npm run test
---> Running in beadc36b293a
> node-docker@1.0.0 test /code
> mocha ./**/*.js
Array
#indexOf()
✓ should return -1 when the value is not present
1 passing (9ms)
Removing intermediate container beadc36b293a
---> 445b80e59acd
Successfully built 445b80e59acd
Successfully tagged node-docker:latest
```
Ive truncated the output again for simplicity but you can see that our tests are run and passed. Lets break one of the tests and observe the output when our tests fail.
Open the util/math.js file and change line 12 to the following.
Open the test/test.js fiole and change line 5 as follows.
```shell
11 function subtract( num1, num2 ) {
12 return num2 - num1
13 }
1 var assert = require('assert');
2 describe('Array', function() {
3 describe('#indexOf()', function() {
4 it('should return -1 when the value is not present', function() {
5 assert.equal([1, 2, 3].indexOf(3), -1);
6 });
7 });
8 });
```
Now, run the same docker build command from above and observe that the build fails and the failing testing information is printed to the console.
```shell
$ docker build -t node-docker --target test .
> [test 3/3] RUN npm run test:
#11 0.509
#11 0.509 > node-docker@0.1.0 test /code
#11 0.509 > mocha ./**/*.js
#11 0.509
#11 0.811
#11 0.813
#11 0.815 Calculator
#11 0.815 adding
#11 0.817 ✓ should return 4 when adding 2 + 2
#11 0.818 ✓ should return 0 when adding zeros
#11 0.818 subtracting
#11 0.822 1) should return 4 when subtracting 4 from 8
#11 0.823 ✓ should return 0 when subtracting 8 from 8
#11 0.823
#11 0.824
#11 0.824 3 passing (14ms)
#11 0.824 1 failing
#11 0.824
#11 0.827 1) Calculator
#11 0.827 subtracting
#11 0.827 should return 4 when subtracting 4 from 8:
#11 0.827
#11 0.827 AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
#11 0.827
#11 0.827 -4 !== 4
#11 0.827
#11 0.827 + expected - actual
#11 0.827
#11 0.827 --4
#11 0.827 +4
#11 0.827
#11 0.827 at Context.<anonymous> (util/math.test.js:18:14)
#11 0.827 at processImmediate (internal/timers.js:461:21)
docker build -t node-docker --target test .
Sending build context to Docker daemon 22.35MB
Step 1/8 : FROM node:14.15.4 as base
---> 995ff80c793e
...
Step 8/8 : RUN npm run test
---> Running in b96d114a336b
> node-docker@1.0.0 test /code
> mocha ./**/*.js
Array
#indexOf()
1) should return -1 when the value is not present
0 passing (12ms)
1 failing
1) Array
#indexOf()
should return -1 when the value is not present:
AssertionError [ERR_ASSERTION]: 2 == -1
+ expected - actual
-2
+-1
at Context.<anonymous> (test/test.js:5:14)
at processImmediate (internal/timers.js:461:21)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! node-docker@1.0.0 test: `mocha ./**/*.js`
npm ERR! Exit status 1
...
executor failed running [/bin/sh -c npm run test]: exit code: 1
```
## Next steps
In this module, we took a look at creating a general development image that we can use pretty much like our normal command line. We also set up our Compose file to map our source code into the running container and exposed the debugging port.
In this module, we took a look at running tests as part of our Docker image build process.
In the next module, well take a look at how to set up a CI/CD pipeline using GitHub Actions. See: