模拟/存根仅在闭包

时间:2016-02-13 20:07:46

标签: javascript unit-testing ecmascript-6 mocha chai

首先,为了测试我的库,我正在使用Mocha和Chai,但我可能在某个时候也需要Sinon。

这是图书馆:

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;

这基本上是测试文件:

import MyLib from '../my-lib.js';

describe('MyLib', () => {
    describe('a method that uses the service', () => {
        ...

service对象对远程服务器进行了一些调用,我在测试中无法做到这一点。因此,我认为我应该存根服务的方法或模拟整个服务对象。但是,由于对象是常量并且只能通过MyLib闭包到达,我不知道如何。

理想情况下,我不希望将MyLib的API更改为例如在构造函数中注入服务对象。

如果重要的话,我会使用Babel 6和es2015预设。

我该如何处理?

3 个答案:

答案 0 :(得分:2)

有几种方法可以做到。

没有额外库的最简单方法

将服务保存为类属性并从那里调用它:

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;

然后你可以在测试中重写服务实例:

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
});

模拟require()函数

有些库允许您覆盖require()函数的结果。我最喜欢的是proxyquire。您可以使用它,您的模块将获得mockedSerice而不是real:

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
});

使用rewire访问模块关闭

Rewire是一个特殊的库,用于检测模块代码,因此您可以在那里更改任何局部变量

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
});

此外,还有一个babel-plugin-rewire可以更好地与您的工具集成。

上述所有方法都很好,您可以选择对您的问题更好的方法。

答案 1 :(得分:1)

我最近也在处理同样的事情。 您可以利用 https://nodejs.org/api/modules.html#modules_caching 在您正在编写的测试中,需要/导入服务的方式与您在正在测试的文件中所做的相同。 然后使用 Sinon 存根您在文件中使用的方法进行测试。

sinon.stub(Service, put).returns()

当文件需要服务时,将使用修改后的模块。

我还没有测试过你的具体情况,你正在创建服务的实例,然后才使用它,但是稍微玩一下它应该可以帮助你实现你想要的,而且这一切都没有外部库,只有简单的 sinon 存根。

答案 2 :(得分:0)

MyLib的测试不应该测试service,因此我建议模拟全部或大部分内容。您应该检查MyLib是否使用正确的参数调用service上的正确函数。

我不确定sinon,但这看起来像是导入和使用库的一种非常标准的方式,如果它不支持模拟service,我会感到惊讶。