Mar*_*ark 48 javascript testing mocking jestjs
正确模拟以下示例的最佳方法是什么?
问题是在导入时间之后,foo保持对原始未引用的引用bar.
module.js:
export function bar () {
return 'bar';
}
export function foo () {
return `I am foo. bar is ${bar()}`;
}
Run Code Online (Sandbox Code Playgroud)
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"
*/
});
});
Run Code Online (Sandbox Code Playgroud)
谢谢!
编辑:我可以改变:
export function foo () {
return `I am foo. bar is ${bar()}`;
}
Run Code Online (Sandbox Code Playgroud)
至
export function foo () {
return `I am foo. bar is ${exports.bar()}`;
}
Run Code Online (Sandbox Code Playgroud)
但这是p.在我看来,到处都是丑陋的:/
Joh*_*lip 22
问题似乎与您期望如何解决bar的范围有关.
一方面,在module.js导出两个函数(而不是一个持有这两个函数的对象).由于模块的导出方式,对导出的东西的容器的引用exports就像你提到的那样.
另一方面,您处理导出(您别名module),就像一个持有这些函数的对象并尝试替换其中一个函数(函数栏).
如果仔细观察你的foo实现,你实际上是对bar函数有一个固定的引用.
当您认为用新的替换了bar函数时,您实际上已经替换了module.test.js范围内的参考副本
为了让foo实际使用另一个版本的bar,你有两种可能:
在module.js中导出一个类或一个实例,同时保存foo和bar方法:
Module.js:
export class MyModule {
function bar () {
return 'bar';
}
function foo () {
return `I am foo. bar is ${this.bar()}`;
}
}
Run Code Online (Sandbox Code Playgroud)
请注意在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');
});
});
Run Code Online (Sandbox Code Playgroud)就像你说的那样,重写全局exports容器中的全局引用.这不是推荐的方法,因为如果没有正确地将导出重置为其初始状态,您可能会在其他测试中引入奇怪的行为.
Mos*_*faR 14
另一种解决方案是将模块导入到自己的代码文件中,并使用所有导出实体的导入实例.像这样:
import * as thisModule from './module';
export function bar () {
return 'bar';
}
export function foo () {
return `I am foo. bar is ${thisModule.bar()}`;
}
Run Code Online (Sandbox Code Playgroud)
现在mocking 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');
});
});
Run Code Online (Sandbox Code Playgroud)
将模块导入自己的代码看起来很奇怪,但由于ES6支持循环导入,它的工作非常顺利.
我遇到了同样的问题,并且由于项目的 linting 标准,exports即使 linting 定义没有阻止,定义类或重写其中的引用也不是代码审查可批准的选项。我偶然发现一个可行的选择是使用babel-rewire-plugin,它至少在外观上要干净得多。虽然我发现它在我可以访问的另一个项目中使用,但我注意到它已经在我在此处链接的类似问题的答案中。这是从链接答案中提供的针对此问题(并且不使用间谍)进行调整的片段以供参考(除了删除间谍之外,我还添加了分号,因为我不是异教徒):
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);
});
});Run Code Online (Sandbox Code Playgroud)
小智 9
从这个线程:
尝试使用函数表达式
export const bar = () => {
return "bar"
}
Run Code Online (Sandbox Code Playgroud)
这应该可以让您监视bar,即使它被同一模块中的另一个函数使用。
首先,我确定的解决方案是通过设置默认参数来使用依赖项注入。
所以我会改变
export function bar () {
return 'bar';
}
export function foo () {
return `I am foo. bar is ${bar()}`;
}
Run Code Online (Sandbox Code Playgroud)
至
export function bar () {
return 'bar';
}
export function foo (_bar = bar) {
return `I am foo. bar is ${_bar()}`;
}
Run Code Online (Sandbox Code Playgroud)
这并不是对组件API的重大更改,通过执行以下操作,我可以轻松覆盖测试中的bar
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');
});
});
Run Code Online (Sandbox Code Playgroud)
这样的好处是也会导致更好的测试代码:)
小智 6
对我有用:
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);
});
});
Run Code Online (Sandbox Code Playgroud)