# Testing Node and Express

This tutorial looks at how to test an Express CRUD app with Mocha and Chai. Although we’ll be writing both unit and integration tests, the focus will be on the latter so that the tests run against the database in order to test the full functionality of our app. Postgres will be used, but feel free to use your favorite relational database.

Let’s get to it!

## Contents

1. Objectives
2. Why Test?
3. Project Setup
4. Database Setup
5. Test Structure
6. Schema Migrations
7. Database Seed
8. Integration Tests
9. Unit Tests
10. Test Fixtures
11. Validation
12. Refactor
13. Conclusion

## Objectives

By the end of this tutorial, you will be able to…

1. Discuss the benefits of automating tests
2. Set up a project with knex.js
3. Write schema migration files with knex to create new database tables
4. Generate database seed files with knex and apply the seeds to the database
5. Perform the basic CRUD functions on a RESTful resource with knex methods
6. Set up the testing structure with Mocha and Chai
7. Write integration tests
8. Write unit tests
9. Write tests, and then write just enough code to pass the tests
10. Create Express routes
11. Practice test driven development
12. Create a CRUD app, following RESTful best practices
13. Generate fake test data (test fixtures) with faker.js
14. Validate request parameters with express-validator

## Why Test?

Are you currently manually testing your app?

When you push new code do you manually test all features in your app to ensure the new code doesn’t break existing functionality? How about when you’re fixing a bug? Do you manually test your app? How many times - ten, twenty, thirty times?

Stop wasting time!

If you do any sort of manual testing write an automated test instead. Your future self will thank you.

Need more convincing? Testing…

1. Helps break down problems into manageable pieces
2. Forces you to write cleaner code
3. Prevents over coding
4. Let’s you sleep at night (because you actually know that your code works)

## Project Setup

To quickly create an app boilerplate install the following generator:

Make sure you have Mocha, Chai, Gulp, and Yeoman installed globally as well:

Create a new project directory, and then run the generator to scaffold a new app:

Open the project in your favorite text editor, and then review the project structure as the dependencies are installed:

Finally, let’s run the app to make sure all is well:

Navigate to http://localhost:3000/ in your favorite browser. You should see:

## Database Setup

Make sure the Postgres database server is running, and then create two new databases in psql, for development and testing:

Install Knex and pg:

Run knex init to generate a new knexfile.js file in the project root, which is used to store database config. Update the file like so:

Here, different database configuration is used based on the app’s environment, either development or test. The environment variable NODE_ENV is used to change the environment. NODE_ENV defaults to development, so when we run our tests, we’ll need to update the variable to test in order to pull in the proper config.

Next, let’s init the database connection. Create a new folder within “server” called “db” and then add a file called knex.js:

The database connection is established by passing the proper environment (via the environment variable NODE_ENV) to knexfile.js which returns the associated object that is passed to the knex library in the third line above.

NOTE: Now is a great time to init a new git repo and make your first commit!

## Test Structure

With that complete, let’s look at the current test structure. In the project root, you’ll notice a “test” directory, which as you probably guessed contains the test specs. Two sample tests have been created, plus there is some basic configuration set up for JSHint and JSCS so that the code is linted against the style config and conventions defined in the .jscsrc and jshintrc files, respectively.

Run the tests:

They all should pass:

Glance at the sample tests. Notice how we updated the environment variable at the top of each test:

Remember what this does? Scroll back up to the previous section if you forgot. Now, when we run the tests, knex is initialized with the test config.

## Schema Migrations

To keep the code simple, let’s use one CRUD resource - users:

Endpoint HTTP Method CRUD Method Result
users GET CREATE get all users
users/:id GET CREATE get a single user
users/:id PUT UPDATE update a single user
users/:id DELETE DELETE delete a single user

Init a new knex migration:

This command created a new migration file in the “src/server/db/migrations” folder. Now we can create the table along with the individual fields:

Field Name Data Type Constraints
id integer not null, unique
email string not null, unique
created_at timestamp Not null, default to current date and time

Add the following code to the migration file:

Apply the migration:

Make sure the schema was applied within psql:

## Database Seed

We need to seed the database to add dummy data to the database so we have something to work with. Init a new seed, which will add a new seed file to “src/server/db/seeds/”:

Update the file:

Run the seed:

Then make sure the data is in the database:

Set up complete.

## Integration Tests

We’ll be taking a test first approach to development, roughly following these steps for each endpoint:

1. Write test
2. Run the test (it should fail)
3. Write code
4. Run the test (it should pass)

Start by thinking about the expected input (JSON payload) and output (JSON object) for each RESTful endpoint:

Endpoint HTTP Input Output
users GET none array of objects
users/:id GET none single object
users POST user object single object
users/:id PUT user object single object
users/:id DELETE none single object

The input user object will always look something like:

Likewise, the output will always have the following structure:

Create a new file in the “test/integration” directory called “routes.users.test.js” and add the following code:

What’s happening here? Think about it on your own. Turn to Google if necessary. Still have questions? Comment below.

With that, let’s start writing some code…

### GET ALL Users

Take note of the inline code comments. Need more explanation? Read over Testing Node.js With Mocha and Chai. Run the test to make sure it fails. Now write the code to get the test pass, following these steps:

#### Update the route config (src/server/config/route-config.js)

Now we have a new set of routes set up that we can use within src/server/routes/users.js, which we need to add…

#### Set up new routes

Create the users.js file in “src/server/routes/”, and then add in the route boilerplate:

Now we can add in the route handler with the knex methods for retrieving all users from the users table:

Run the tests:

You should see the test passing:

### GET Single User

Moving on, we can just copy and paste the previous test and use that boilerplate to write the next test:

Run the test. Watch it fail. Write the code to get it to pass:

Test:

Code:

Test:

Code:

### DELETE

Test:

Code:

Run all your tests. All should pass.

## Unit Tests

We need a route to return all users created after a certain date. Since we already know how to write routes, let’s add a helper function that takes an array of users and a year that then returns an array of users created on or after the specified date. We can then use this function in a future route handler.

Steps:

1. Write a unit test
2. Run the tests (the unit test should fail)
3. Write the code to pass the test
4. Run the tests (all should pass!)

### Write a unit test

Create a new file called controllers.users.test.js within the “test/unit/” directory:

Now add the body of the test:

#### What’s happening?

Within the it block we passed in the userArray, a year, and a callback function to a function called filterByYear(). This then asserts that a error does not exist and that the length of the response (total) is 2.

Run the tests. Watch them fail. Add the code…

### Write the code to pass the unit test

Create a new controller within “src/server/controllers” called users.js:

Confused? Add an inline comment above each line, describing what the code does and, in some cases, why the code does what it does.

Run the tests. Do they pass? They should.

Now you can use that function in a new route to finish the business requirement. Do this on your own. Be sure to write the integration test first!

## Test Fixtures

faker.js is a powerful library for generating fake data. In our case, we can use faker to generate test data for our unit tests. Such data is often called a test fixture.

Install faker:

### Code

Since we don’t really (or maybe really don’t?) know what the test is going to look like, let’s start with writing a quick script to generate test data. Create a new file within the “test” directory called generate.test.data.js, and then add the following code to it:

This function generates an array of user objects, each object is generated randomly using faker.js methods. We could use that data directly in the test, but let’s first save the data to a fixture file for easy use. Update the file like so:

Now we can generate two arrays -

1. One with data before a specific date
2. The other with data on or after that specific date

Before saving to a file, we concatenated the two arrays into one. You’ll see why this was necessary when the test is created. For now, run the script from the project root:

You should see a new fixture file called test.data.json in the “test” folder. The data in this file can now be used in a test.

### Test

Add the following test to controllers.users.test.js:

Make sure to require in fs and path at the top:

Run the tests again!

## Validation

Thus far we have not tested for possible errors. For example, what happens if the email address provided with a POST request is not properly formatted? Or if an invalid ID is used with a PUT request?

We can start by validating parameters with express-validator.

### Install

Then require the module at the top of src/server/config/main-config.js:

Mount the validator to the app middleware just below the body parser:

### Test

Add the first test to the GET /api/v1/users/:id' describe block:

Add the second test to the POST /api/v1/users describe block:

### Code

To add the proper validation, create a new file called validation.js within the “routes” directory:

Here, with express-validator, parameters are validated using either req.checkParams or req.checkBody and then errors are aggregated together with req.validationErrors().

Require this module in the user routes:

Add the validateUserResources to all the route handlers except the handler to GET ALL users, like so:

Finish the remaining route handlers, and then run the tests. All should pass:

We are still not handling all errors. What else could go wrong? Think about edge cases. Then write tests. Do this on your own.

## Refactor

Finally, let’s make our code a bit more modular by refactoring out unnecessary logic from the route handlers.

Within the “db” folder, add a file called queries.users.js:

This function now handles all the knex logic, and it can now be used anywhere in the project. Be sure to update the route handler:

Don’t forget to add the requirement:

Run the tests again. They all should still pass! Notice how we were able to refactor with confidence (without fear of the code breaking) since we had proper test coverage. Finish refactoring out all of the knex logic to queries.users.js. Test again when done.

## Conclusion

Turn back to the objectives. Read each aloud to yourself. Can you put each one into action?

The testing process may seem daunting and unnecessary at first, but you will see just how necessary it is as your projects grow and become more complex. Continue to practice by incorporating tests whenever you begin a new project.

The full code can be found in the express-testing-mocha-knex repository.