Michel Bitter
Michel Bitter

Reputation: 95

Unit testing in Typescript with Dependency Injection

I'm running into problems with typescript during unit testing. I try to unit test an module like the following code example:

import {Express} from 'express'

export interface BootClassInterface {
  setup(): Express
}

export class BootClass implements BootClassInterface {

  constructor(private express: () => Express ){

  }

  public test(){
      const app = this.express()
      app.get("/test", (req, res) => {
        res.send("Hello World")
      })

      return app;
  }
}

For testing propose I want to know if express().get() was called and if the first parameter was '/test'. Before I switched to typescript I always used the module sinonJS to spy or stub the functionality so that I could test of a certain dependency was used correctly. Now with Typescript I run into problems with the strict types that I've set in the module. By example:

import * as chai from 'chai'
import 'mocha'
import * as sinon from 'sinon'
import * as express from 'express'

import * as Boot from '../../src/Core/Boot'

const assert = chai.assert

suite('[CORE] unit test /Core/Boot.ts', () => {
  let BootClass: Boot.BootClassInterface

  setup(() => {
    const expressApp = () => {
      const app = express()
      app.get = sinon.spy()
      return app
    }

    const Services: Boot.BootServicesInterface = { express: expressApp }
    BootClass = new Boot.BootClass(Services)

  })

  test('Do first test', () => {
    const app = BootClass.setup()
    chai.assert.equal(app.get.called, 1)
  })
})

The code example above results in the following Typescript compiling error:

error TS2339: Property 'called' does not exist on type 'ApplicationRequestHandler<Express>'.

I can see why typescript returns this error and somehow it is even obvious. I even know a possible solution where I accept Express as any in the module.

But I'm looking for a more elegant way to be able to spy/stub/mock my dependencies for testing propose, but at the same time have the advantages of strict typing.

Upvotes: 3

Views: 1673

Answers (1)

atoth
atoth

Reputation: 858

AS you have stated you specify Express as the return type on BootClassInterface interface. Therefore app would be considered Express and it will look up its properties instead of your mock.

You can also just cast one property of app as any. Try:

 chai.assert.equal((<any>app.get).called, 1)

Or using Sinon type definition:

 chai.assert.equal((<SinonSpy>app.get).called, 1)

Upvotes: 2

Related Questions