In this challenge you will re-implement testing on the Noteful app, this time we a real database. And you'll update the tests to cross-check the API results against the database.
- NPM install devDependencies for testing and update
package.json
file - Update server.js to prevent
app.listen
from running during tests - Add a
test
property to knexfile.js - Create a test database
- Create Seed Data
Install the main dependencies you need for testing
mocha
the testing frameworkchai
the assertion librarychai-http
achai
plug-in which allows you to makehttp
requests to the APIcross-env
which provides a convenient way to set environment variables across platforms
npm install mocha chai chai-http cross-env --save-dev
Add "test": "mocha"
to your scripts
property in package.json
. Notice, the cross-env
command will set the NODE_ENV
to test
when run your test suite using the npm test
command.
"scripts": {
"start": "node server.js",
"test": "cross-env NODE_ENV=test mocha"
},
Wrap the app.listen
call in a require.main === module
conditional to prevent it from running during tests. And add module.exports = app;
to export your app to make it available to your test suite.
// 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
Tests need to run against a separate test database. Create a noteful-test
database.
createdb -U dev noteful-test
Or, if you have difficulties with the createdb
then you can run CREATE DATABASE
in the psql
shell.
psql -U dev noteful-app
CREATE DATABASE "noteful-test";
Update the knexfile.js
to include a test
property with a TEST_DATABASE_URL
which defaults to your local test database.
The complete knexfile.js
will look like this.
module.exports = {
development: {
client: 'pg',
connection: process.env.DATABASE_URL || 'postgres://localhost/noteful-app',
debug: true, // http://knexjs.org/#Installation-debug
pool: { min: 1, max: 2 }
},
test: {
client: 'pg',
connection: process.env.TEST_DATABASE_URL || 'postgres://localhost/noteful-test',
pool: { min: 1, max: 2 }
},
production: {
client: 'pg',
connection: process.env.DATABASE_URL
}
};
In the ./db
directory, create a seedData.js
file and copy in the following code.
const knex = require('../knex');
const util = require('util');
const exec = util.promisify(require('child_process').exec);
module.exports = function(file, user = 'dev') {
return exec(`psql -U ${user} -f ${file} -d ${knex.client.connectionSettings.database}`);
};
Next, create the /test/server.test.js
file. And copy this gist into the file.
The file contains a suite of tests which are similar to the solution form the previous challenge. The main difference are the Mocah life-cycle hooks.
before
runs before all the tests in thedescribe
blockbeforeEach
runs before each test in thedescribe
blockafterEach
runs after each test in thedescribe
blockafter
runs after all the tests in thedescribe
block
beforeEach(function () {
return seedData('./db/noteful.sql', 'dev');
});
after(function () {
return knex.destroy(); // destroy the connection
});
Run your tests using npm test
to confirm the setup is correct.
npm test
You should see a set of successful tests. If any of the test fail, please fix them before proceeding.
Now you are ready to update your test suite to to cross check the API results against the database. Below are 4 examples which show common approaches. Use these are guides to implement your own integrations tests
In server.test.js
file, find the test with the description should return the default of 10 Notes
and update it to the following. The test will first query the database to get the count of notes, then call the API and verify the response length is the same as the database count
it('should return the default of 10 Notes ', function () {
let count;
return knex.count()
.from('notes')
.then(([result]) => {
count = Number(result.count);
return chai.request(app).get('/api/notes');
})
.then(function (res) {
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.be.a('array');
expect(res.body).to.have.length(count);
});
});
Find the test with the description should return correct search results for a valid query
and update it to the following. In this test you will call to the API with a searchTerm
and then perform the same query against the database. The test And compare the results. You'll chain these requests using .then()
.
it('should return correct search results for a valid query', function () {
let res;
return chai.request(app).get('/api/notes?searchTerm=gaga')
.then(function (_res) {
res = _res;
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.be.a('array');
expect(res.body).to.have.length(1);
expect(res.body[0]).to.be.an('object');
return knex.select().from('notes').where('title', 'like', '%gaga%');
})
.then(data => {
expect(res.body[0].id).to.equal(data[0].id);
});
});
In the example you'll query the database and call the api then compare the results. But this time you'll use Promise.all()
so the async requests can be made simultaneously, and you can compare the results with create a variable at a higher scope. Find the test with the description should return correct notes
and update to with the following.
it('should return correct notes', function () {
const dataPromise = knex.first()
.from('notes')
.where('id', 1000);
const apiPromise = chai.request(app)
.get('/api/notes/1000');
return Promise.all([dataPromise, apiPromise])
.then(function ([data, res]) {
expect(res).to.have.status(200);
expect(res).to.be.json;
expect(res.body).to.be.an('object');
expect(res.body).to.include.keys('id', 'title', 'content');
expect(res.body.id).to.equal(1000);
expect(res.body.title).to.equal(data.title);
});
});
And when creating, you will .post()
the new document to the endpoint, then using the id returned, query the database and verify it was saved correctly. Find the test with the correct description and update it with the following
it('should create and return a new item when provided valid data', function () {
const newItem = {
'title': 'The best article about cats ever!',
'content': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor...',
'tags': []
};
let body;
return chai.request(app)
.post('/api/notes')
.send(newItem)
.then(function (res) {
body = res.body;
expect(res).to.have.status(201);
expect(res).to.have.header('location');
expect(res).to.be.json;
expect(body).to.be.a('object');
expect(body).to.include.keys('id', 'title', 'content');
return knex.select().from('notes').where('id', body.id);
})
.then(([data]) => {
expect(body.title).to.equal(data.title);
expect(body.content).to.equal(data.content);
});
});
Your Turn!
Using the patterns described above, update the rest of the tests for Notes to verify the changes have been persisted to the DB. Then create similar tests for Folders and Tags.
Open or create a .travis.yml
file and add the following configuration. This configuration informs Travis that you intend to use a postgresql
database. And it provides a script to create the test database.
language: node_js
node_js: node
services: postgresql
before_script:
- psql -U postgres -c 'CREATE DATABASE "noteful-test";'
Complete the CDCD configuration process. You can follow the[ CICD guides
You have 2 options to configure Heroku with Postgres: ElephantSQL and Heroku's Postgres Add-on.
On ElephantSQL, create a new database for your production site and copy the postgres connection URL.
In your terminal, run the following command to create your tables and insert sample data. You'll need to replace the sample connection with the URL you copied above
psql -f ./db/noteful.sql postgres://<UN>:<PW>@baasu.db.elephantsql.com:5432/<DB>
On Heroku open your application and go to the setting. Then click "Reveal Config Vars" button. In the KEY field type "DATABASE_URL" and in the VALUE field paste in the ElephantSQL connection URL. You may need to restart the dynos for the change to take effect. Look under the "More" dropdown for the restart dynos option.
On Heroku, open your app and go to the Resources tabs. Then the Add-ons section, enter "Postgres" in the search box and select "Heroku Postgres". And then provision a "Hobby Dev - Free" instance of postgres.
Once it is provisions click on "Heroku Postgres :: Database". This should open a new window/tab. Go to settings and click "View Credentials..." and copy the postgres URI.
In your terminal, run the following command to create your tables and insert sample data. You'll need to replace the sample connection with the Postgres URI you copies above
psql -f ./db/noteful.sql postgres://<UN>:<PW>@baasu.db.elephantsql.com:5432/<DB>
On Heroku open your application and go to the setting. Then click "Reveal Config Vars" button. In the KEY field type "DATABASE_URL" and in the VALUE field paste in the ElephantSQL connection URL. You may need to restart the dynos for the change to take effect. Look under the "More" dropdown for the restart dynos option.