Writing unit tests in TypeScript
In this story, we would be using TypeScript for unit testing along with popular frameworks: Mocha/Chai, Jasmine or Jest. You have decided the framework and want to write unit tests in TypeScript, Great! We would walk through changes required to support unit tests in TypeScript. If not, documentation of each of this libraries can be referred. Writing test cases in TypeScript is very much same as it is in JavaScript. The most important part is to do setting up so that test cases written in TypeScript can be executed using this libraries.
The source code is available at https://github.com/chiragrupani/TSUnitTestsSetup. It contains setup and examples for each of these frameworks.
The setup is very simple, we would install respective test framework and their types. We would be using ts-node (for Mocha and jasmine) and ts-jest (for Jest) to add TypeScript support. We would be using nyc for code coverage.
We would follow the conventions: Place Source JS/TS files in src
folder and tests typescript files in tests
folder.
Basically, it is installation of npm packages for TypeScript, Test framework (e.g. Jasmine/Mocha/Jest) and specifying test script required to execute test cases as explained further. Along with selected unit test framework package, the corresponding types also required to be installed. For executing TS tests in Node, we need to specify Scripts
for test
in package.json
. The package.json
file is located under root of project and is generated when you execute npm init
.
To debug TypeScript tests, the json specified under “VS Code debug” section below in the story need to be added under configurations
in launch.json
which can be created by going to Debug Menu and then Add Configuration in VS Code. Below are npm commands, test script and VS code debug recipe for each framework:
Mocha/Chai
NPM Install Command
npm i -D chai mocha nyc ts-node typescriptnpm i -D @types/chai @types/mocha
Test Script
"scripts": {
"test": "mocha -r ts-node/register tests/**/*.test.ts", "coverage": "nyc -r lcov -e .ts -x \"*.test.ts\" npm run test"
}
VS Code Debug
{
"type": "node",
"request": "launch",
"name": "Mocha Current File",
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"args": [
"--no-timeouts",
"--colors",
"${file}",
"--require",
"ts-node/register"
],
"console": "integratedTerminal",
"sourceMaps": true,
"internalConsoleOptions": "neverOpen"
}
Sample Test
describe('calculate', function() {
it('add', function() {
let result = Calculator.Sum(5, 2);
expect(result).equal(7);
});
});
Jasmine
NPM Install Command
npm i -D jasmine nyc ts-node typescriptnpm i -D @types/jasmine
Test Script
"scripts": {
"test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json","coverage": "nyc -r text -e .ts -x \"tests/*.test.ts\" npm run test"}
The jasmine.json
at root directory specifies path for tests like below:
{
"spec_dir": "tests",
"spec_files": ["**/*[tT]est.ts"]
}
VS Code Debug
{
"type": "node",
"request": "launch",
"name": "Jasmine Current File",
"program": "${workspaceFolder}/node_modules/jasmine/bin/jasmine",
"args": [
"${workspaceFolder}/TSOutput/tests/${fileBasenameNoExtension}.js"
],
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": ["${workspaceFolder}/TSOutput/**/*.js"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
Sample Test
describe('calculate', function() {
it('add', function() {
let result = Calculator.Sum(5, 2);
expect(result).toBe(7);
});
});
Jest
NPM Install Command
npm i -D @babel/core @babel/preset-env @babel/preset-typescript babel-jest jest typescriptnpm i -D @types/jest
Test Script
"scripts": {
"test": "jest", "coverage": "jest --coverage"
}
VS Code Debug
{
"type": "node",
"request": "launch",
"name": "Jest Current File",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": ["${relativeFile}"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
}
}
Note: Test script here is just “jest”. To use TypeScript, We define config jest.config.js
and babel.config.js
file at location where package.json
resides
jest.config.js
module.exports = {
testEnvironment: 'node',
testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node']
};
babel.config.js
module.exports = {
presets: [
['@babel/preset-env', { targets: { node: 'current' } }],
'@babel/preset-typescript',
],
};
The key is testRegex
, where we are matching ts/tsx files ending with test/spec under tests folder. We are using ‘node’ in ‘testEnvironment’ since we are executing tests on Node (which makes it faster), else we would be using default ‘jsdom’ value.
Sample Test
describe('calculate', function() {
it('add', function() {
let result = Calculator.Sum(5, 2);
expect(result).toBe(7);
});
That’s all required for set up, tests can be run by executing command:
npm t
npm t
is shortcut for npm run test
and for getting coverage results:
npm run coverage
Adding Test Case
Before adding unit test cases, first let us understand Suite and Specs. Spec is each individual test case and contains one or many assertions. The test case passes when its expectations are true. It is generally defined (based on test framework) by using it
functions. it contains two parameter — one is name of test case and other function containing assertion. Suite is group of related specs and generally defined using describe
similar to it
and contains many it functions. Sample test case above shows how it is defined for each of the framework.
Executing some code before (test initialization) and after (cleanup) each test cases, mocking external objects/services etc depends on framework, documentation for framework would contain required information.
Thanks for reading, feel free to share and tap on clap button, if you find it useful and please add comments if you need any further help in setting up TypeScript test cases or if I missed anything.