Translate

Friday 30 March 2018

Testing a typescript class with mocha

Platform: nodejs
Install typescript, mocha for nodejs and the associated typings for mocha. 

The story starts with coming across some mocha problem caused by the usage of the implements keyword in Typescript in one of the forums for techies.

As ever, I picked it up and decided to write a mocha test to see what was the problem.

The test result revelations were as interesting as the Cambridge analytica fiasco and the subsequent handling of it in Singapore!

Some premise before I take you through to the bumper details!
.
An interface, in Typescript, serves as the blueprint much like in the other languages but it serves an additional purpose of defining how classes would implement them.

For instance, for a class that accepts a locale and a local time value, if the interface types the two parameters as a string and a number, scoped public respectively, then the class is forced to apply the same constraints. Even implementing the public property as private is not allowed.


// IClockTime.ts

export default interface IClockTime {
    new (time: number): IClock;
}

// IClock.ts

export default interface IClock {
    currentTime: number;
    locale: string;  
}

// Clock.ts

import IClock from './IClock'
import IClockTime from './IClockTime'
export default class Clock implements IClock {
    constructor(public currentTime:string) { 
        this.currentTime=currentTime
        }        
    public tellTime(t:IClockTime){
            return new t(this.currentTime);
        }
}

// AnotherClock.ts

import IClock from './IClock'
import IClockTime from './IClockTime'
export default class AnotherClock implements IClock {
    constructor (public currentTime: string, count: number) {
        this.currentTime=currentTime
        this.count=count
    }
    public tellTime(t:IClockTime){
            return new t(this.currentTime);
        }
}

// interfacestest.ts

import ClockTime from './IClockTime'
import AnotherClock from './anotherclock'
import Clock from './clock';
import {expect} from 'chai'
import 'mocha'
describe('an interface test', () => {  
it('to check time', () => {
var cc: ClockTime=Clock;
var cc1:ClockTime=AnotherClock
var ci = new cc(9.15,"GMT",12);
    expect(ci.tellTime(Clock)).to.equal('9:15');
  });
});


The tsc compiler is shrewd! It has noticed that the AnotherClock class has gone ahead and implemented some member of its own and not followed the blueprint provided by the IClock interface. This is much in the comfort zone of C# programmers.

It also correctly identifies what all AnotherClock has incorrectly implemented.

Modify the anotherClock class like so:

// AnotherClock.ts

import IClock from './IClock'
import IClockTime from './IClockTime'
export default class AnotherClock implements IClock {
   private _currentTime:number;
    private _locale:string;
    public get currentTime():number{
        return this._currentTime
    }
    public set currentTime(newtime:number){
        this._currentTime=newtime
    }
    private get locale():string{
        return this._locale
    }    
    private set locale(newlocale:string){
        this._locale=newlocale
    }
    constructor (currentTime: number, locale: string) {
        this.currentTime=currentTime
        this._locale=locale
    }
    public tellTime(){
            return this.currentTime;
        }
}

And you get errors from tsc that the property locale is not in sync with how the interface published it!


I will scope this to an example of a test of the class behavior and test whether the Clock returns the time expected of it, given the locale.

The Clock class, too, attempts its own tricks by supplying a string to a parameter of type IClockTime when it types the parameter of the checkLocale() method as a IClockTime rather than as a string. Change the parameter from t:IClockTime to t:string.

Modify the Clock class like so:

import IClock from './IClock'
import IClockTime from './IClockTime'
export default class Clock implements IClock {
    private _currentTime:number;
    private _locale:string;
    public get currentTime():number{
        return this._currentTime
    }
    public set currentTime(newtime:number){
        this._currentTime=newtime
    }
    constructor(currentTime:number, public locale:string) { 
        this.currentTime=currentTime
        this._locale=locale
        }        
    private checkLocaleTime(t:string){
        if (this._locale==="GMT")
        this.currentTime=this.currentTime-5.00
        
    }
    public tellTime(){
            this.checkLocaleTime(this.locale)
            console.log(this.currentTime)
            return this.currentTime.toString();
        }
}


Modify the test like so:

// interfacetest.ts

import ClockTime from './IClockTime'
import anotherClock from './anotherclock'
import Clock from './clock';
import IClock from './IClock'
import {expect} from 'chai'
import 'mocha'
describe('an interface test', () => {  
it('to check time', () => {
let cc= new Clock(9.15,"GMT");
    expect(cc.tellTime()).to.equal('9:15');
  });
});



Modify the expectation to match the GMT (approx.!) time and make the test pass!

Happy typescripting interfaces and classes for ES5!

No comments: