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 in JavaScript. The most important part is to do set 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), jasmine-ts (for 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 typescript
npm i -D @types/chai @types/mocha

Test Script

"scripts": {
"test": "mocha -r ts-node/register tests/**/*.test.ts",
    "testWithCoverage": "nyc -r lcov -e .ts -x \"*.test.ts\" mocha -r ts-node/register tests/**/*.test.ts && nyc report"
}

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 jasmine-ts nyc ts-node typescript
npm i -D @types/jasmine

Test Script

"scripts": {
"test": "jasmine-ts --config=jasmine.json",
  "testWithCoverage": "nyc -r lcov -e .ts -x \"*.test.ts\" jasmine-ts --config=jasmine.json && nyc report"
}

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-ts/lib/index",
"args": ["${file}"],
"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 jest ts-jest typescript
npm i -D @types/jest

Test Script

"scripts": {
"test": "jest",
  "testWithCoverage": "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 transform in new config jest.config.js file at location where package.json resides

module.exports = {
transform: {'^.+\\.ts?$': 'ts-jest'},
testEnvironment: 'node',
testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node']
};

The key is testRegex , where we are matching ts/tsx files ending with test/spec under tests folder. It will processed by ts-jest . 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 testWithCoverage
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 itfunctions. 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.