Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript / Node.js - How to mock transitive dependencies for integration testing?

Let say I have an Express route which is handled by a controller. The controller uses a service and the service uses a repository to talk to a data source.

I want to create an integration test using Supertest to test that route :

test -> my Express app -> controller -> service -> repository -> data source

My problem is that I need to mock the repository/data source, to run the test! I want to hardcode some values as if they were coming from the real data source. What are my options?

In a Java world, I would be using dependency injection with Spring or Guice and I would replace the repository with a mock version that way. What are the patterns to achieve such mocking in a Typescript/Node.js world?

I guess using plain Javascript I could use Proxyquire and its Globally override require feature, to mock the repository from the test itself. But I'm not sure this works well with Typescript.

So what are the recommended ways of mocking a "deep" component (a transitive dependency) from within a test file, using Typescript and Node.js?

like image 205
electrotype Avatar asked Feb 27 '17 23:02

electrotype


1 Answers

Modules are cached after the first time they are loaded, so you can just load them first in your test file and stub them using a library like sinon.

Consider the following code:

// dependency.ts
export function foo(){
    return 'foo';
}

// app.ts
import {foo} from './dependency';
export default function main(){
    return  'winner ' + foo();
}

You could test app.ts stubbing its dependency with sinon in the following way:

import * as Dependency from '../src/dependency';
import main from '../src/app';

describe('test dependency', () => {
    var fooStub;
    beforeEach(() => {
        fooStub = sinon.stub(Dependency, 'foo');
        fooStub.returns('la la lang');
    });
    afterEach(()=>{
        fooStub.restore();
    })

    it('uses stubbed dependency', ()=>{
        expect(main()).to.be.equal('winner la la lang');
    });

    it('can return different values on other tests', ()=>{
        fooStub.returns('moonlight');
        expect(main()).to.be.equal('winner moonlight');
    });
});

So basically for your integration tests you can import and stub your dependencies before starting the app. I have done this creating an app.proxy.ts file:

  • In app.proxy import your repository and stub it to return the predefined data. After setting up the stub, import the real app.ts and export it.
  • In your integration test file, import app.proxy instead of app and use it with supertest. This will give you the app in the end, but after setting up the stubbed dependency!
  • write and run the test, knowing it will use the predefined data
like image 146
Daniel J.G. Avatar answered Nov 19 '22 10:11

Daniel J.G.