How to separate mocha tests in multiple files
- Introduction
- Step 1. Understand mocha hooks
- Step 2. Create a test module
- Step 3. Setup our test folder structure
- Conclusions
Introduction
One of my latest tasks was to unit test a central module in our application; there were some tests but not as many as needed. We go for at least 75% code coverage.
The most significant issues were that the test file rapidly grew up to 2000 lines of code which is unacceptable. Maintaining large files of code is an arduous task.
We concluded that we need to separate our test suite in multiple files.
The source code for the following tutorial can be found at mocha-tutorial
Step 1. Understand mocha hooks
For this study, we are creating a small project to have a better feeling how mocha loads the before
and after
hooks.
Let's create a small test case with the following structure:
- hooks/
- first/
before-first.ts
- second/
before-second.ts
some.test.ts
before-hooks.ts
Source code for before-hooks.ts
before(() => {
console.log('Hooks level');
});
Source code for before-first.ts
before(() => {
console.log('First level');
});
Source code for before-second.ts
before(() => {
console.log('Second level');
});
Source code for some.test.ts
import assert from 'assert';
describe('Some simple test', () => {
it('should return true', () => {
assert.strictEqual(true, true);
});
});
After we compile and run the code with mocha ./dist --recursive
we receive the following result:
Hooks level
First level
Second level
Some simple test
✓ should return true
1 passing (6ms)
We noticed that mocha supports multiple
before
hooks and they run first in the order of the folder structure.
This means that we can set up our system in the
before
hooks and use it in the child folders.
Step 2. Create a test module
The following code represents our module to be tested.
// /lib/subject.ts
import { Dependency1 } from './dependency1';
import { Dependency2 } from './dependency2';
export class Subject {
constructor(private dep1: Dependency1, private dep2: Dependency2) { }
public do(param: string) {
return param;
}
}
Dependency1
and Dependency2
are just 2 empty classes as following:
// /lib/dependency1
export class Dependency1 {
}
Step 3. Setup our test folder structure
The test folder structure will be the following
- test/
- lib/
- subject/
- suites/
first.test.ts
second.test.ts
third.test.ts
index.ts
before-subject.ts
The source code for before-subject.ts
// /test/lib/subject/before-subject.ts
import { Dependency1 } from '../../../lib/dependency1';
import { Dependency2 } from '../../../lib/dependency2';
import { Subject } from '../../../lib/subject';
before(function() {
const dep1 = new Dependency1();
const dep2 = new Dependency2();
// We are attaching the subject instance to mocha this
// We will get the subject instance during the test run
this.subject = new Subject(dep1, dep2);
});
The source code for first.test.ts
// /test/lib/subject/suites/first.test.ts
import assert from 'assert';
export default function suite() {
it('should return "good" when sending "good"', function() {
// We are receiving the injected subject from the this.test.ctx
// which are set up during the before hooks
const result = this.test.ctx.subject.do('good');
assert.strictEqual(result, 'good');
});
}
So to wrap our test the source code for index.ts
// /test/lib/subject/index.ts
import first from './suites/first.test';
import second from './suites/second.test';
import third from './suites/third.test';
describe('Subject', function() {
describe('first suite', first.bind(this));
describe('second suite', second.bind(this));
describe('third suite', third.bind(this));
});
When we build and run our unit tests we will receive something like:
Subject
first suite
✓ should return "good" when sending "good"
second suite
✓ should return "bad" when sending "bad"
third suite
✓ should return "the ugly" when sending "the ugly"
3 passing (6ms)
Conclusions
- Mocha supports multiple
before/after
hooks in child folders also. - We can set up our test class in
before
hooks by attaching to thethis
instance, and we will be able to extract it init
functions atthis.test.ctx