Introduction
The main goal of dividing tests into multiple files is to make tests readable and easier to maintain. Each file should be responsible for a single responsibility. Here is how I separate tests into multiple files:
Step by step
First, we need to specify which files mocha will test. I personally create*.test.js
to specify that mocha will only run files with .test.js
. So another file that should be a part of the main test will be ignored. For this example, I will create a math module.
Let's say our project has math_problem.js
file in src
as our module.
const sumData = (a, b, c = 0) => {
return a + b + c;
};
module.exports = { sumData };
I assume our project already has mocha
and chai
in project dependencies. Complete source code available in my GitHub repository
Test in separate test files
For the first problem, create part of test 1. problem_one.part.js
in tests/modules
directory
const { sumData } = require("../../src/math_problem");
const { expect } = require("chai");
const problem_one_test = () => {
it("has to be 4", () => {
const result = sumData(2, 2);
expect(result).eql(4);
});
};
module.exports = problem_one_test;
Then for the test file create function.test.js
in tests
directory.
process.env.NODE_ENV = "testing";
const problemOne = require("./modules/1. problem_one.part");
describe("math problem", () => {
describe("problem one", problemOne.bind(this));
});
In this file, I force Node environment to testing
, it is not required for this project, but this would help if you need a specific environment for testing.
Finally in your package.json
add test command in scripts
section
"scripts": {
"test": "mocha tests/*.test.js --exit"
},
Done!.
Now you can run mocha test in separate files by run npm test
or npm run test
.
Pass parameter to separate test files
Let's say you already create a variable that would be required for test part files. To pass a variable from main test file to part file, we create a parent function.
Now, create 2. problem_two.part.js
in tests/moduels
directory.
const { sumData } = require("../../src/math_problem");
const { expect } = require("chai");
const problem_two_test = (c) => {
return function () {
it("has to be 8", () => {
const result = sumData(2, 2, c);
expect(result).eql(8);
});
};
};
module.exports = problem_two_test;
Assume in our test, c
variable will be added from c
param in problem_two_test
function. So c
variable will be required for tests available in 2. problem_two.part.js
file.
In function.test.js
file, import problem two by adding code below after problemOne
variable
const problemTwo = require("./modules/2. problem_two.part")(4);
and add another decribe test for problemTwo
describe("problem two", problemTwo.bind(this));
Sofunction.test.js
file will be like this
process.env.NODE_ENV = "testing";
const problemOne = require("./modules/1. problem_one.part");
const problemTwo = require("./modules/2. problem_two.part")(4);
describe("math problem", () => {
describe("problem one", problemOne.bind(this));
describe("problem two", problemTwo.bind(this));
});
Done, Now we can pass variable from main function to test part file.
in this line
const problemTwo = require("./modules/2. problem_two.part")(4);
we pass4
asc
param in2. problem_two.part.js
file. co variableproblemTwo
will return test functions and already hasc
param.
Ability for single file test
Breaking tests into multiple test files will benefit easier and more readable code. But how if you want to test only a single file before being added to the main test case? We don't want to test every test just to make sure that a specific test is right.
In order to test a single test file running, we create describe test in every part files but if only it run in single test command. So to make a difference from batch test, in function.test.js
we set a TEST_MODE
environment variable. Add process.env.TEST_MODE = "batch";
below NODE_ENV.
So function.test.js
file will be like this:
process.env.NODE_ENV = "testing";
process.env.TEST_MODE = "batch";
const problemOne = require("./modules/1. problem_one.part");
const problemTwo = require("./modules/2. problem_two.part")(4);
describe("math problem", () => {
describe("problem one", problemOne.bind(this));
describe("problem two", problemTwo.bind(this));
});
And for each part files, add describe test, so each file will be like this below.
1. problem_one.part.js
file:
const { sumData } = require("../../src/math_problem");
const { expect } = require("chai");
const problem_one_test = () => {
it("has to be 4", () => {
const result = sumData(2, 2);
expect(result).eql(4);
});
};
if (process.env.TEST_MODE !== "batch") {
describe("problem one", problem_one_test.bind(this));
}
module.exports = problem_one_test;
2. problem_two.part.js
file:
const { sumData } = require("../../src/math_problem");
const { expect } = require("chai");
const problem_two_test = (c) => {
return function () {
it("has to be 8", () => {
const result = sumData(2, 2, c);
expect(result).eql(8);
});
};
};
if (process.env.TEST_MODE !== "batch") {
describe("problem one", problem_two_test(4).bind(this));
}
module.exports = problem_two_test;
In package.json
file, add test-single
command, so scripts
section will be like this
"scripts": {
"test": "mocha tests/*.test.js --exit",
"test-single": "mocha --exit"
},
now you can test each file by run command like this
npm run test-single-env '.\tests\modules\1. problem_one.part.js'
Load .env files
What if you have a .env
file for testing? You want to create a specific environment for your test. There are many ways to load Node environment from .env
file. You will need dotenv
dependency here. Assume you have already installed it in your project. For example, we create third test case.
Create 3. problem_three.part.js
in tests/modules
directory. Write code like below
const { sumData } = require("../../src/math_problem");
const { expect } = require("chai");
const problem_three_test = () => {
it("has to be 10", () => {
const result = sumData(2, 2, Number(process.env.STATIC_NUMBER));
expect(result).eql(10);
});
};
if (process.env.TEST_MODE !== "batch") {
describe("problem three", problem_three_test.bind(this));
}
module.exports = problem_three_test;
Now add test case in functions.test.js
file
process.env.NODE_ENV = "testing";
process.env.TEST_MODE = "batch";
const problemOne = require("./modules/1. problem_one.part");
const problemTwo = require("./modules/2. problem_two.part")(4);
const problemThree = require("./modules/3. problem_three.part");
describe("math problem", () => {
describe("problem one", problemOne.bind(this));
describe("problem two", problemTwo.bind(this));
describe("problem three", problemThree.bind(this));
});
Dotenv will be loaded in script command, so make test command that require dotenv/config in package.json
file. I will define specific file .env.
for testing environment.
In .env.testing
file, assume we have STATIC_NUMBER environment variable
STATIC_NUMBER=6
For package.json
file add script command below
"scripts": {
"test": "mocha tests/*.test.js --exit",
"test-single": "mocha --exit",
"test-env": "mocha -r dotenv/config tests/*.test.js --exit dotenv_config_path=./.env.testing",
"test-single-env": "mocha -r dotenv/config --exit dotenv_config_path=./.env.testing"
},
Now the test-env
and test-single-env
commands will automatically load .env.testing
file to environment variable.
You might notice a warning message
Cannot find any files matching pattern "dotenv_config_path=./.env.testing"
. This is because mocha also read this as spec test file param.
Reference
- Project repository: Github
- Related Article: How to separate mocha test