Integration tests with databases (Node.js + Mocha)

Automation tests are divided into several categories. To be short, unit tests are used to test small fragments of code. For example, there is a function for formatting a phone number. We might have several unit tests for covering various scenarios, but if we want to check how user performs registration with this number and then passes authorization, we need to cover interaction of several components in our test – this is an integration test (or maybe even acceptance test).

Generally, we are facing an integration test, if it uses:

  • A database
  • A network
  • Any external system (e.g. a mail server)
  • I/O operations

The hard part is that unlike unit tests we cannot run test operations directly on external systems. E.g. we cannot send thousands of test mails to randomly generated addresses. there are several ways to solve this kind of problem depending on what we want to test. let’s look at the options:

 

Service imitation (Stubs, Mocks)

Let’s assume we’re writing a client application, which invokes services on various servers. I.e. our priority is testing a client and no need to actually use production operations. In this case we can create a service stub with exactly same functions and parameters as the real one. only instead of executing the real logic, it will return some fixed responses.

function sendMail(email, content) {
    console.log(‘Email sent to: ‘ + email);
    return true;
}

When we run our app in a test mode, we should make it use the fake service object instead of a real one (Let’s dive into details in future articles).

 

Using the database

Let’s say we are writing a service which heavily uses a database and we need integration tests to check it. clearly we can substitute the database layer with a stub and let select, insert,etc. operations return some predefined fixed values. However in most cases this is not practical and doesn’t really test the relations among various processes. For instance, I would like user to register, activate their account and perform authorization. This flow uses several tables and I would prefer to execute it on the database.

There are several solutions here, too. I prefer to have an empty database separately – neither in-memory, nor a lighter alternative, but exactly the same version of a database, just dedicated to testing. When my app runs in a test mode, it will fetch the test database path from corresponding configuration and will use for test operations. First it will clear the tables to avoid broken state.

I will use Node and Mocha for this example

In my previous post I was describing configuration of various environments. I don’t think of Mocha tests as a different environment, because we might have dev, test and even build servers and tests would be running on all of them. However I will follow the similar method – I’ll use environment variables for configuring testing runtime, too, and I’ll create .env.mocha file.

I’d like to note that the dotenv documentation clearly states – it’s not recommended to have multiple env files like .env, env.test, env.prod, etc, but we should have one .env file with different content on different servers. In my opinion .env.mocha serves completely different purpose and is not included in this rule.

The next step is to use .env.mocha file instead of a real one while app runs in a test mode. Currently there is no working cross-platform code on the internet and I like using Windows OS, so I’m offering my solution, and no need to load configuration in every test file either:

  • Create .env.mocha file in the project directory and configure properly with test values.
  • Create setup.js file under test directory and put this line into it:
    require('dotenv').config({path:__dirname + '/../.env.mocha'});
  • Create one more file under test directory – mocha.opts and put this line there:
    --require test/setup.js

That’s it.
When you run ‘npm test’ on the project, .env.mocha configuration will be used in every test automatically.

For the sake of insurance and to make sure that I’m not loading production configuration (not to drop all databases), I’ll add one more property into the .env.mocha file and execution of setup.js will continue only in case it is found (e.g. MOCHA_CONFIG_LOADED=yes)

I would also like to have empty tables before running tests. Mocha has various hooks and among them before(), which will be invoked before executing the test suite, if we put it inside ‘describe’. If we declare it globally, then it will be executed only once before all tests. That’s exactly what I need. It would be better if I could put this code in setup.js, but if you try, you’ll find that mocha is not yet loaded on that stage and ‘before’ variable won’t be defined. So, I added hooks.js file under the test directory and described my global hooks there.

If integration tests take too long to execute, it’s possible to configure scripts in package.json and make different commands for running unit and integration tests (separated on a directory level).

გამოხმაურება

comments

Leave a Reply

Your email address will not be published. Required fields are marked *