Automated tests - BDD for your Express Node JS backend

Express based Node.JS backend

I love BDD.  It made my life as a developer 1000x better. Just because I can fix and create tests way before the product reaches QA time. Here are three of the most common types of automated tests:

  • Unit tests: A single piece of code (usually an object or a function) is tested, isolated from other pieces
  • Integration tests: Multiple sections are examined together, for example, testing database access code against a test database
  • Acceptance tests: (also called Functional tests): Automatic testing of the entire application, for example using a tool like Selenium to run a browser automatically.


BDD is TDD on steroids

Let's see first how you can do TDD - Test Driven Development; you need to change the way you develop. Here are the steps you start coding a feature:

  1. Start by writing a test
  2. Run the test and any other tests. At this point, your newly added test should fail. If it doesn’t fail here, it might not be testing the right thing and thus has a bug in it.
  3. Here you start working at the feature: write the minimum amount of code required to make the test pass.
  4. Run the tests to check the new test passes
  5. Optionally refactor your code
  6. Go back to step no 1

It can take some effort to learn well, but spending the time can pay off big. TDD projects often get a code-coverage of 90-100%.

BDD – Behavior-Driven Development – is perhaps the most significant source of confusion. When applied to automated testing, BDD is a set of best practices for writing great tests. BDD can and should be, used together with TDD and unit testing methods. Lab test - 26K downloads at the time of writing - is easy to use the library for BDD, TDD, unit and integration tests. Let's start first by installing it:

npm i --save --only=dev lab


Just to show you an example you can see below how we test the signup and login in an end to end test for a full stack kit.

suite('[test][User][integrations]', () => {    
    //...some other setup code => read the full article for details
    test('User should be able to register', async (done) => { 
		const email = faker.internet.email();            
        const password = faker.internet.password();            
        const firstName = faker.name.firstName();            
        const lastName = faker.name.lastName();             
		let response = await request(app).
					post('/api/users/signup').
					send({email: email, password: password, name: lastName, surname: firstName}).
					set('Accept', 'application/json');
		expect(response.status).to.equal(200);            
        const user = response.body;            
		expect(user.email).to.equal(email);       
		expect(user.password).to.equal(password);          
		expect(user.name).to.equal(lastName);           
		expect(user.surname).to.equal(firstName);            
        //Now let's see if the user is able to login            
		response = await request(app).
					post('/api/users/login').
					send({user:{email: email, password: password}}).
					set('Accept', 'application/json');        
		expect(response.status).to.equal(200);    

    });}//end of test

});//end of suite

This can also be applied from a frontend perspective but will detail that in a later article.

Unrelated to this subject: In Appseed PRO version you can also get everything set up for easily debugging tests in VSCode. You can even get all the above code for free and use it yourself. Licensed MIT.

Every test suite comes with some callback that executes each time before and after the suite.

before(function () {    
    //initialize database    
    //create global variables    
    //mock data    
    //inject
});

after(async (done) => {    
    //do a global cleanup of the database
    //some async stuff here
    done();
});

Also, before and after each test you can register, create new entities and clean up those entries.

beforeEach(function () {    
	//get the models required for each subtest
    //example: create an account with signup before 
	//checking other features that requires login session
});

afterEach( function () {    
	//do something after each test
});

Another library that you can use for testing is faker - 780K weekly downloads at the time of writing - that creates random data for the internet. For example, you can generate emails, names, passwords and location addresses.
This simulates very easy the real environment, so you don't lose precious time on finding actual data. It can also give you fake id's, but that is not something I would recommend. Don't forget to install faker in your node js app:

npm i --save --only=dev faker
Supertest - 720K downloads at the time of writing - helps you skip doing server injections and call HTTP methods directly from a test. Having a simple endpoint that handles HTTP calls helps us use supertest library.

npm i --save --only=dev supertest


To use supertest you have to export your express app object from index.js

const app = express();
app.use(require('./routes'));
// some middleware setup code ...
module.exports = app; 

We are now ready to use lab tests just by running the command:

npm run test

This prepares our project for CI and CD with Travis. The next must-have in 2019.

Show Comments

Get the latest posts delivered right to your inbox.