For this challenge you will create a full suite of tests that confirm your app is working correctly. Then you'll setup Continuous Integration to ensure your app continues to work.
- Install mocha, chai and chai-http
- Update
server.js
to support tests - Create a suite of tests:
- Test that the environment
- Test the static server
- Test the endpoints
- Setup continuous integration with Travis CI
To get started you need to install Mocha and Chai as dev dependencies and wire-up NPM to execute mocha
Mocha is a testing framework, its job is to find and run your tests. It provides a functions, describe
and it
to make it easy to write and document tests. It also provides the formatting output called "reporters" as well as life-cycle hooks: before
, beforeEach
, after
and afterEach
which you'll use when we discuss databases.
Chai is an assertion library, its job is to provide methods help you verify a given value matches the expected value. Chai provides three flavors of assertion styles: Assert, Should and Expect. We'll use the Expect style.
Type the following in your shell. Be sure to add the --save-dev
flag and check package.json
to ensure the packages were saved properly.
npm install --save-dev mocha chai
In package.json
, add the following test
command to the scripts
property.
Open the package.json
file and add the following test
script property.
...
"scripts": {
"start": "node server.js",
"test": "mocha"
},
...
Rhe scripts
property allows NPM to run the command. The start
and test
commands are just a couple of the pre-defined properties which NPM supports. For these scripts you can simply run npm start
and npm test
, respectively. For custom scripts you need use npm run [SCRIPT NAME]
. See scripts docs for more info.
In your shell, run npm test
. You should see the following message.
$ npm test
> mocha
No test files found
npm ERR! Test failed. See above for more details.
Mocha ran successfully, but it could not find any test files. Let's fix that now.
Next, create a test file with a couple tests to confirm the packages are loaded and configured properly.
Create /test/server.test.js
and add the following:
const chai = require('chai');
const expect = chai.expect;
describe('Reality check', function () {
it('true should be true', function () {
expect(true).to.be.true;
});
it('2 + 2 should equal 4', function () {
expect(2 + 2).to.equal(4);
});
});
Back in the shell, run npm test
again. You should see the following output:
$ npm test
> mocha
Reality Check
✓ true should be true
✓ 2 + 2 should equal 4
2 passing (6ms)
Congrats, you've setup and run your first tests.
Next you will create tests to confirm the Express Static Server is working properly but you'll need to perform some additional setup first.
In server.js
wrap the app.listen
call with the following condition if (require.main === module) {...}
and then export the app
.
// Listen for incoming connections
if (require.main === module) {
app.listen(PORT, function () {
console.info(`Server listening on ${this.address().port}`);
}).on('error', err => {
console.error(err);
});
}
module.exports = app; // Export for testing
The above requires some explaining. When running the server, (e.g. npm start
or node server.js
) node sets the require.main
to the current module. That means that it is easy to determine if a file has been run directly by testing require.main === module
. Basically, the if (require.main === module) {...}
condition prevents the server from automatically starting when we run the tests.
The module.exports = app;
exports the Express app so it can be required and used in your test files.
Chai has a plug-in named chai-http
which allows you to make requests, send data, and receive the responses from endpoints.
In your shell, install chai-http
to the devDependencies
npm install --save-dev chai-http
In /test/server.test.js
, require the app
and chai-http
, then inform chai
to use chai-http
. At this point, the top of your file should look like this.
const app = require('../server');
const chai = require('chai');
const chaiHttp = require('chai-http');
const expect = chai.expect;
chai.use(chaiHttp);
describe('Reality check', function () {
// REMOVED FOR BREVITY
});
Run npm test
again to verify the tests are still working properly.
Your test environment is now ready to test Express. Below the "Reality check" tests, add the following two tests:
describe('Express static', function () {
it('GET request "/" should return the index page', function () {
return chai.request(app)
.get('/')
.then(function (res) {
expect(res).to.exist;
expect(res).to.have.status(200);
expect(res).to.be.html;
});
});
});
describe('404 handler', function () {
it('should respond with 404 when given a bad path', function () {
return chai.request(server)
.get('/DOES/NOT/EXIST')
.then(res => {
expect(res).to.have.status(404);
});
});
});
Run your tests to confirm everything is working properly.
Your challenge is to create tests for the rest of the endpoints. You should try to test both positive (eg 2XX) and negative (eg 4XX) outcomes.
Below is a list of requirements to help guide you.
-
Static server
- GET request "/" should return the index page
-
404 handler
- should respond with 404 when given a bad path
-
GET /api/notes
- should return the default of 10 Notes as an array
- should return an array of objects with the
id
,title
andcontent
- should return correct search results for a valid query
- should return an empty array for an incorrect query
-
GET /api/notes/:id
- should return correct note object with
id
,title
andcontent
for a given id - should respond with a 404 for an invalid id (
/api/notes/DOESNOTEXIST
)
- should return correct note object with
-
POST /api/notes
- should create and return a new item with location header when provided valid data
- should return an object with a message property "Missing
title
in request body" when missing "title" field
-
PUT /api/notes/:id
- should update and return a note object when given valid data
- should respond with a 404 for an invalid id (
/api/notes/DOESNOTEXIST
) - should return an object with a message property "Missing
title
in request body" when missing "title" field
-
DELETE /api/notes/:id
- should delete an item by id
Good luck!
Excellent, you have a test suite! Now, let's set up CICD. If you prefer, here are command-line only instructions for the CLI Ninjas.
Before starting, make sure you have Travis and Heroku command line clients installed
On a Mac: gem install travis
Configure Travis CI to run your test suite when you or your teammates push changes to GitHub. Sign into Travis CI with your GitHub account.
Note: Free (org) vs Paid (com): Be sure to use Travis CI's free service on .org rather than the paid service not .com.
Activate Travis CI <> GitHub connection:
- On Travis CI:
- Go to Profile: User (in upper-right) > Accounts
- Click "Sync Account"
- If the repo is in a GitHub organization opposed to your personal account then select the organization from the menu.
- Activate repo by toggling the switch
Verify a "Travis CI" setup on GitHub
- On GitHub:
- Go to Settings > Integrations & Services
- Verify a "Travis CI" under Services section
- Go to Settings > Integrations & Services
In the root of your project, create a file name .travis.yml
and add the following.
language: node_js
node_js: node
Commit the file and push repo to Github
git add -A
git commit -m 'add .travis.yml'
git push origin master
On Travis CI:
- Watch build complete successfully :-)
Next you'll create an app on Heroku and configure it to automatically deploy when Travis CI tests pass
- Login to Heroku and create an app
- Click on the "Deploy" tab
- Click "GitHub" (in the Deployment Method section)
- Select the correct account and search for your repo
- Click "Connect"
- Select "Wait for CI to pass before deploy" checkbox
- Click "Enable Automatic Deploys"
Back on your repo, make a change to any file and commit and push to trigger new build.
git commit -am 'setup heroku'
git push origin master
Add a Travis CI badge to your repo:
- On Travis CI, find your project and click on the badge
- Change the dropdown menu to "Markdown" and copy the output
- Add the badge code to your
readme.md
file, commit and push - The code looks something like this:
[![Build Status](https://travis-ci.org/<USERNAME>/<REPO-NAME>.svg?branch=master)](https://travis-ci.org/<USERNAME>/<REPO-NAME>)
You can view an example solution and compare the differences between branches
Good Luck!
The code below Test Static Server.... for the second describe block....shouldnt the
be
?