如何嘲笑的功能在同一个使用模块开玩笑

问题描述 投票:37回答:6

什么是正确的嘲笑下面的示例中的最佳方式是什么?

问题是,进口时间后,foo保持参照原unmocked bar

module.js:

export function bar () {
    return 'bar';
}

export function foo () {
    return `I am foo. bar is ${bar()}`;
}

module.test.js:

import * as module from '../src/module';

describe('module', () => {
    let barSpy;

    beforeEach(() => {
        barSpy = jest.spyOn(
            module,
            'bar'
        ).mockImplementation(jest.fn());
    });


    afterEach(() => {
        barSpy.mockRestore();
    });

    it('foo', () => {
        console.log(jest.isMockFunction(module.bar)); // outputs true

        module.bar.mockReturnValue('fake bar');

        console.log(module.bar()); // outputs 'fake bar';

        expect(module.foo()).toEqual('I am foo. bar is fake bar');
        /**
         * does not work! we get the following:
         *
         *  Expected value to equal:
         *    "I am foo. bar is fake bar"
         *  Received:
         *    "I am foo. bar is bar"
         */
    });
});

谢谢!

编辑:我可以改变:

export function foo () {
    return `I am foo. bar is ${bar()}`;
}

export function foo () {
    return `I am foo. bar is ${exports.bar()}`;
}

但是这是对P。丑陋在我看来的确无处不在:/

javascript testing mocking jestjs
6个回答
19
投票

这个问题似乎涉及您如何期待吧的范围加以解决。

一方面,在module.js导出两个函数(代替的物体保持这两个函数)。由于路模块输出的参考的出口事物的容器exports像你提到它。

在另一方面,你处理你的出口(您化名module)一样的物体保持这些功能,并试图取代它的功能之一(功能栏)。

如果您在您的富落实仔细观察你实际上是拿着固定的参考吧的功能。

当你认为你有更换一个新的酒吧功能,您只需更换实际参考副本在你module.test.js范围

为了使FOO实际使用酒吧的另一个版本,有两种可能性:

  1. 在module.js导出一个类或一个实例,同时握住foo和bar方法: Module.js: export class MyModule { function bar () { return 'bar'; } function foo () { return `I am foo. bar is ${this.bar()}`; } } 注意在foo的方法使用该关键字。 Module.test.js: import { MyModule } from '../src/module' describe('MyModule', () => { //System under test : const sut:MyModule = new MyModule(); let barSpy; beforeEach(() => { barSpy = jest.spyOn( sut, 'bar' ).mockImplementation(jest.fn()); }); afterEach(() => { barSpy.mockRestore(); }); it('foo', () => { sut.bar.mockReturnValue('fake bar'); expect(sut.foo()).toEqual('I am foo. bar is fake bar'); }); });
  2. 就像你说的,改写全球exports容器中的全球基准。这是不要去,你很可能会在其他测试中引入怪异的行为,如果你没有正确地重置出口到初始状态的推荐方式。

14
投票

另一种解决方案可以导入模块插入它自己的代码文件,并使用所有导出的实体的进口情况。像这样:

import * as thisModule from './module';

export function bar () {
    return 'bar';
}

export function foo () {
    return `I am foo. bar is ${thisModule.bar()}`;
}

现在嘲讽bar是很容易的,因为foo还使用bar的出口情况:

import * as module from '../src/module';

describe('module', () => {
    it('foo', () => {
        spyOn(module, 'bar').and.returnValue('fake bar');
        expect(module.foo()).toEqual('I am foo. bar is fake bar');
    });
});

导入模块插入它自己的代码看起来很奇怪,但由于ES6对循环进口的支持,它的作品真的顺利。


6
投票

FWIW,我看中了是使用dependency injection,通过设置默认参数的解决方案。

所以,我会改变

export function bar () {
    return 'bar';
}

export function foo () {
    return `I am foo. bar is ${bar()}`;
}

export function bar () {
    return 'bar';
}

export function foo (_bar = bar) {
    return `I am foo. bar is ${_bar()}`;
}

这并不是我的组件的API重大更改,我可以很容易地通过执行以下操作覆盖在我的测试吧

import { foo, bar } from '../src/module';

describe('module', () => {
    it('foo', () => {
        const dummyBar = jest.fn().mockReturnValue('fake bar');
        expect(foo(dummyBar)).toEqual('I am foo. bar is fake bar');
    });
});

这有导致轻微更好的测试代码的好处太:)


2
投票

我有这个同样的问题,由于该项目的掉毛标准,定义一个类或改写在exports引用没有代码审查,即使不掉毛定义防止可批准的选项。我无意中发现了一个可行的办法是使用babel-rewire-plugin这是干净多了,至少在外表。当我发现这在另一个项目中,我不得不使用的访问,我发现它已经在我所挂here类似的问题的答案。这是从参考链接的答案提供调整这个问题(和不使用间谍)摘要(我还添加了除分号去除间谍,因为我不是一个异教徒):

import __RewireAPI__, * as module from '../module';

describe('foo', () => {
  it('calls bar', () => {
    const barMock = jest.fn();
    __RewireAPI__.__Rewire__('bar', barMock);
    
    module.foo();

    expect(bar).toHaveBeenCalledTimes(1);
  });
});

https://stackoverflow.com/a/45645229/6867420


1
投票

如果你定义的出口之后就可以引用你的功能部分的出口对象。然后你就可以在你的嘲笑个别覆盖的功能。这是由于进口的工作方式作为参考,而不是副本。

module.js:

exports.bar () => {
    return 'bar';
}

exports.foo () => {
    return `I am foo. bar is ${exports.bar()}`;
}

module.test.js:

describe('MyModule', () => {

  it('foo', () => {
    let module = require('./module')
    module.bar = jest.fn(()=>{return 'fake bar'})

    expect(module.foo()).toEqual('I am foo. bar is fake bar');
  });

})

0
投票

对我的作品:

cat moduleWithFunc.ts

export function funcA() {
 return export.funcB();
}
export function funcB() {
 return false;
}

cat moduleWithFunc.test.ts

import * as module from './moduleWithFunc';

describe('testFunc', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  afterEach(() => {
    module.funcB.mockRestore();
  });

  it.only('testCase', () => {
    // arrange
    jest.spyOn(module, 'funcB').mockImplementationOnce(jest.fn().mockReturnValue(true));

    // act
    const result = module.funcA();

    // assert
    expect(result).toEqual(true);
    expect(module.funcB).toHaveBeenCalledTimes(1);
  });
});
© www.soinside.com 2019 - 2024. All rights reserved.