Writing unit tests in TypeScript (by the example of cats)

How do I write unit tests in a TypeScript project? In this article I will try to answer this question and also show you how to create a unit testing environment for projects using TypeScript.





What are unit tests?

Unit tests - Tests applied across different layers of an application that test the smallest divisible logic in an application: for example, a module, class, or method.





, , , () .





. , . . , , . .





. :





project
| node_modules
| src
| package.json
| tsconfig.json
      
      



./src



cat.module.ts



Cat.





export class Cat {
  public name: string;
  public color: string;

  constructor(name: string, color: string) {
    this.name = name;
    this.color = color;
  }

  public move(distanceMeter: number) : string {
    return `${this.name} moved ${distanceMeter}m.`;
  }

  public say() : string {
    return `Cat ${this.name} says meow`;
  }
}
      
      



, . (SUT - system under test).





, test, .





npm :





npm install --save-dev ts-node mocha @testdeck/mocha nyc chai @types/chai







:

ts-node



- TypeScript REPL node.js.





mocha



- , , . . @testdeck/mocha



- testdeck , .





nyc



- CLI Istanbul, .





chai



- (assertions), . . @types/chai



typescript'





, test



, tsconfig.json



, TS .





{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "baseUrl": "./",
    "module": "commonjs",
    "experimentalDecorators": true,
    "strictPropertyInitialization": false,
    "isolatedModules": false,
    "strict": false,
    "noImplicitAny": false,
    "typeRoots" : [
      "../node_modules/@types"
    ]
  },
  "exclude": [
    "../node_modules"
  ],
  "include": [
    "./**/*.ts"
  ]
}
      
      



include test .ts





, register.js



ts-node, .





const tsNode = require('ts-node');
const testTSConfig = require('./test/tsconfig.json');

tsNode.register({
  files: true,
  transpileOnly: true,
  project: './test/tsconfig.json'
});
      
      



, .mocharc.json



, :





{
  "require": "./register.js",
  "reporter": "list"
}
      
      



.nyrc.json



.





{
  "extends": "@istanbuljs/nyc-config-typescript",
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
    "node_modules/"
  ],
  "extension": [
    ".ts"
  ],
  "reporter": [
    "text-summary",
    "html"
  ],
  "report-dir": "./coverage"
}
      
      



, :





project
| node_modules
| src
| test
| --- tsconfig.json
| .mocharc.json
| .nyrc.json
| package.json
| register.js
| tsconfig.json
      
      



package.json







"test": "nyc ./node_modules/.bin/_mocha 'test/**/*.test.ts'"
      
      



,





./test



cat.unit.test.ts



:





import { Cat } from '../src/cat.module';
import { suite, test } from '@testdeck/mocha';
import * as _chai from 'chai';
import { expect } from 'chai';

_chai.should();
_chai.expect;

@suite class CatModuleTest {
  private SUT: Cat;
  private name: string;
  private color: string;

  before() {
    this.name = 'Tom';
    this.color = 'black';
    this.SUT = new Cat(this.name, this.color);
  }
}
      
      



Cat cat.module.ts



, , .





before Cat, .





, .





import { Cat } from '../src/cat.module';
import { suite, test } from '@testdeck/mocha';
import * as _chai from 'chai';
import { expect } from 'chai';

_chai.should();
_chai.expect;

@suite class CatModuleTest {
  private SUT: Cat;
  private name: string;
  private color: string;

  before() {
    this.name = 'Tom';
    this.color = 'black';
    this.SUT = new Cat(this.name, this.color);
  }

  @test 'Cat is created' () {
    this.SUT.name.should.to.not.be.undefined.and.have.property('name').equal('Tom');
  }
}
      
      



npm test :





As you can see, our coverage is not complete, lines 11-15 remained untested. These are just the methods of the Cat class move



andsay.







We add two more tests for these methods and we end up with such a file with tests:





import { Cat } from '../src/cat.module';
import { suite, test } from '@testdeck/mocha';
import * as _chai from 'chai';
import { expect } from 'chai';

_chai.should();
_chai.expect;

@suite class CatModuleTest {
  private SUT: Cat;
  private name: string;
  private color: string;

  before() {
    this.name = 'Tom';
    this.color = 'black';
    this.SUT = new Cat(this.name, this.color);
  }

  @test 'Cat is created' () {
    this.SUT.name.should.to.not.be.undefined.and.have.property('name').equal('Tom');
  }

  @test 'Cat move 10m' () {
    let catMove = this.SUT.move(10);
    expect(catMove).to.be.equal('Tom moved 10m.');
  }

  @test 'Cat say meow' () {
    expect(this.SUT.say()).to.be.equal('Cat Tom says meow');
  }
}
      
      



Run our tests again and see that the Cat class now has full test coverage.





Outcome

As a result, we have created a test infrastructure for our application and now we can cover any new module or class with tests, checking that the existing code has not broken.





PS: Based on the article How setting up unit test with TypeScript .








All Articles