Mocking / drowning objects that are defined only in a closure

First of all, to test my library, I use Mocha and Chai, but I will probably need Sinon for too long.

This is the library:

import Service from 'service'; // a third-party module out of my control const service = Service(...); class MyLib { ... uses `service` in a bunch of different ways ... ... service.put(foo) ... ... service.get(bar) ... } export default MyLib; 

This is basically a test file:

 import MyLib from '../my-lib.js'; describe('MyLib', () => { describe('a method that uses the service', () => { ... 

The service object makes some calls on remote servers, which I cannot do in tests. Therefore, I think I should stifle the service methods or make fun of the whole service object. However, since the object is persistent and only accessible when MyLib closed, I do not know how to do this.

Ideally, I don't want to change the MyLib API, for example. enter the service object in the constructor.

I am using Babel 6 with preset es2015 , if that matters.

How do I approach this?

+6
source share
2 answers

There are several ways to do this.

The easiest way without additional libraries

Save the service as a property of the class and call it from there:

 import Service from 'service'; const service = Service(...); class MyLib { constructor() { this.service = service; } ... now you should call service in a bit different way ... this.service.put(foo) ... ... this.service.get(bar) ... } export default MyLib; 

Then you can rewrite the service instance in your tests:

 it('should call my mock', () => { const lib = new MyLib(); lib.service = mockedService; // you need to setup this mock, with Sinon, for example lib.doSomething(); assert.ok(mockedService.put.calledOnce); // works }); 

Mock require () function

There are several libraries that allow you to override the results of the require() function. My favorite proxyquire . You can use it and your module will get mockedSerice instead of the real one:

 import proxyquire from 'proxyquire'; it('should call my mock', () => { const MyLib = proxyquire('./my-lib', { // pass here the map of mocked imports service: mockedService }) const lib = new MyLib(); lib.doSomething(); assert.ok(mockedService.put.calledOnce); // works }); 

Use rewire to access module closure

Rewire is a special library that encodes the module code, so you can change any local variable there

 import rewire from 'rewire'; it('should call my mock', () => { const MyLib = rewire('./my-lib') const lib = new MyLib(); // __set__ is a special secret method added by rewire MyLib.__set__('service', mockedService); lib.doSomething(); assert.ok(mockedService.put.calledOnce); // works }); 

In addition, there is babel-plugin-rewire for better integration with your tools.

All of the above methods are good, you can choose what is best for your problem.

+2
source

Your tests for MyLib should not test service , so I suggest making fun of all or most of them. You should simply verify that MyLib calls the correct functions on the service with the correct arguments.

I'm not sure about Sinon, but it seems like a pretty standard way to import and use the library, I would be surprised if it didn't support the mockery of service .

0
source

All Articles