Skip to content

Instantly share code, notes, and snippets.

@cklanac
Last active February 6, 2024 22:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cklanac/81a6f49fabb52b3c95dff397fe62c771 to your computer and use it in GitHub Desktop.
Save cklanac/81a6f49fabb52b3c95dff397fe62c771 to your computer and use it in GitHub Desktop.
Challenge 05: Testing with Mocha Chai

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.

Requirements

  • 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

Create Test Suite

To get started you need to install Mocha and Chai as dev dependencies and wire-up NPM to execute mocha

Install Dev Dependencies

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

Create NPM Test Script

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.

Create Basic Tests

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.

Configure App for Testing

Next you will create tests to confirm the Express Static Server is working properly but you'll need to perform some additional setup first.

Update server.js for testing

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.

Install additional dependencies

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.

Test the Static Server

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.

Test the API Endpoints

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 and content
    • 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 and content for a given id
    • should respond with a 404 for an invalid id (/api/notes/DOESNOTEXIST)
  • 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!

Set up Continuous Integration and Continuous Deployment

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.

Install Travis and Heroku Command Line Clients

Before starting, make sure you have Travis and Heroku command line clients installed

Install Travis CI's CLI

On a Mac: gem install travis

Install Heroku CLI

Configure GitHub <> Travis CI Integration

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

Add Travis Config File

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 :-)

Create Heroku App and Configure for Deployment

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

Bonus: Add Travis CI badge to your repo

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>)

Solutions

You can view an example solution and compare the differences between branches

Good Luck!

@Manny1806
Copy link

The code below Test Static Server.... for the second describe block....shouldnt the

return chai.request(server)

be

return chai.request(app)

?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment