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 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.

--

--

--

Full stack .Net developer, Web developer and Web Surfer.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Cypress.io + OneLogin: using API to authenticate before testing

How You Should Not Write JavaScript

Best Practices for Building JavaScript SDKs With TypeScript

How to setup Angular, Material & Firebase

A deep dive into Kentico 12 MVC Widgets with Vue.js

React single page application

Get Rid of Your React Class Component: React Hooks in a Nutshell

Dynamic imports, React and Redux

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Chirag Rupani

Chirag Rupani

Full stack .Net developer, Web developer and Web Surfer.

More from Medium

React with memo: Avoid Performance Pitfalls

“Flat” Promises for Reactive Code

How to base your output type on a generic input type in typescript

Two persons writing on a whiteboard

Overmind, the state management library you don’t know (yet) (but should)