5
votes

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?

1
If you're using plain Node rather than Typescript, there's a beautiful library by the creator of passport called electrolyte which does dependency injection/IoC. You set up folders that it will look up in reverse order using its IoC.use(..) function, so in my test bootstrap file I add my mocks folders so that it looks there first, and will resolve my mock data source instead of the data source in my other folders or node modules.Liam Gray
I created a testing library for Typescript that makes it very easy to mock dependencies, no matter where they are in the dependency chain: ts-mock-importsEmandM

1 Answers

6
votes

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