Vlad Zhavoronkov

Testing in Node.js

In this article we will look through things and instruments for testing for Node.js server. There's brief information about Mocha and Chai frameworks that make testing simple. We will write test for "real-world" server.
Vlad Zhavoronkov
iOS Developer
Instruments:
  1. Mocha - Testing Environment
Mocha is JS framework that allows asynchronous testing. In other words, it provides an environment for tests execution.

Main Mocha features
  • simple async support, including promises;
  • async test timeout support;
  • use any assertion library you want, Chai in our tutorial;
  • before, after, beforeEach, afterEach - interface that Mocha provides for code execution before or after each test;
2. Chai - Assertion Library
Assertion Library is about setting conditions. If one of these conditions fail, then test will also fail.

Main Chai features
should, expect, assert - these keywords are main Chai interface. Meaning of this words is the same as English words meaning. For example serverResponse.should.have.status(200) or expect(res.body).have.property('message') - this is valid code if you use Chai.


Testing Philosophy
Why is it called Unit tests? Because its purpose is to test single unit of application: api's endpoint, service, method.

Best approach for unit test architecture consists of three parts:
  1. Arrange - define all necessary preconditions and inputs;
  2. Act - act on the unit (function, method, object) that need to be tested;
  3. Assert - make sure that expected results occur.
Usage:
As mentioned above, Mocha provides environment for test execution. Mocha usage is very simple, in most cases you just use two keywords:
  1. describe - for creating test environment
  2. it - for setting conditions and defining that coded test cases which it need to pass
The point of Chai is to use simple human-readable interface to increase clarity of tests. to, be, equal, have, not… - all this assertion interface provided by Chai.
Here is a simple test example:
Advanced Usage:
If you want to know more about "real-world" testing, then look at this code below. This example will illustrate common server's API test.

We assume that we got server that has two endpoints and we can make GET request to them: /sum and /square:
Now let's implement test for this server:
What's happening here:

  1. Arrange: Define inputs and expected results.
  2. Act: Go through sums. Make a GET request for each sum with x and y params.
  3. Assert: define expected server-response status and values.
  4. Trick: making it block wait for all server responses. As here we got the queue of server requests, we need to wait until the last request is completed. For this we call done when last server-response is handled.

Can you write test for /square endpoint by your own?

And that's it. Testing is very simple if you know exactly what to test and use common test scenarios such as Arrange-Act-Aside. What we really recommend you now is to dive in mocha and chai documentations to learn some advantage topics of this testing instruments.
Useful notes
Human-readable is important
For this use BDD-style assertions library that provides interface with "human-readable" keywords. The should, expect, assert provided by Chai are perfect regarding this problem.

Be sure of what you testing
Right internals testing such testing server's backend is really expensive in terms of time but not guarantee that external things (like API) will work perfectly.

Meanwhile right external testing such testing server's API isn't such expensive but will test the internal things implicitly and external things explicitly (like API).

Don't use unrealistic data, approximate production conditions
Nice real data generator: Faker.

Simplify your tools, minimize third-party libraries and abstraction
This will help a lot to other readers of your tests.

before and beforeEach aren't equal
Code in before block runs once before all the tests in "parent" describe block Code in beforeEach runs before each test in "parent" describe block. Same for after and afterEach.

Use done for synchronous checks.
Without done calling here it will just dispatch someAsyncWork and return before its callback call. In code above it will return when done get called. In other words, if we call done here then we will get an output "123", otherwise "132".

Tests should be independent from one to another
It is preferable approach when test#1 doesn't depend and effects on test#2 and vise versa.
Thanks for reading!