Jon*_*ins 61 unit-testing ecmascript-6 jestjs
我有以下模块我试图在Jest中测试:
// myModule.js
export function otherFn() {
console.log('do something');
}
export function testFn() {
otherFn();
// do other things
}
Run Code Online (Sandbox Code Playgroud)
如上图所示,这部分出口命名的功能和重要的testFn用途otherFn.
在Jest中我正在编写单元测试时testFn,我想模拟该otherFn函数,因为我不希望错误otherFn影响我的单元测试testFn.我的问题是我不确定最好的方法:
// myModule.test.js
jest.unmock('myModule');
import { testFn, otherFn } from 'myModule';
describe('test category', () => {
it('tests something about testFn', () => {
// I want to mock "otherFn" here but can't reassign
// a.k.a. can't do otherFn = jest.fn()
});
});
Run Code Online (Sandbox Code Playgroud)
任何帮助/见解表示赞赏.
gfu*_*lam 42
jest.requireActual()在里面使用jest.mock()
jest.requireActual(moduleName)返回实际模块而不是模拟,绕过对模块是否应该接收模拟实现的所有检查.
我更喜欢这种简洁的用法,你需要并在以下内容中传播jest.requireActual(moduleName):
// myModule.test.js
jest.mock('./myModule.js', () => (
{
...(jest.requireActual('./myModule.js')),
otherFn: () => {}
}
))
describe(...)
Run Code Online (Sandbox Code Playgroud)
Jest的Manual Mocks文档(接近示例末尾)中也引用了此方法:
为了确保手动模拟及其实际实现保持同步,可能需要
jest.requireActual()在手动模拟中使用真实模块并在导出之前使用模拟函数对其进行修改.
bob*_*obu 28
import m from '../myModule';
Run Code Online (Sandbox Code Playgroud)
对我不起作用,我确实使用过:
import * as m from '../myModule';
m.otherFn = jest.fn();
Run Code Online (Sandbox Code Playgroud)
Bri*_*ams 14
看来我参加这个聚会迟到了,但是可以。
testFn只需otherFn 使用模块进行调用。
如果testFn使用模块调用otherFn,otherFn则可以为模块导出testFn模拟,并将调用模拟。
这是一个工作示例:
myModule.js
import * as myModule from './myModule'; // import myModule into itself
export function otherFn() {
return 'original value';
}
export function testFn() {
const result = myModule.otherFn(); // call otherFn using the module
// do other things
return result;
}
Run Code Online (Sandbox Code Playgroud)
myModule.test.js
import * as myModule from './myModule';
describe('test category', () => {
it('tests something about testFn', () => {
const mock = jest.spyOn(myModule, 'otherFn'); // spy on otherFn
mock.mockReturnValue('mocked value'); // mock the return value
expect(myModule.testFn()).toBe('mocked value'); // SUCCESS
mock.mockRestore(); // restore otherFn
});
});
Run Code Online (Sandbox Code Playgroud)
转换后的代码不允许babel检索otherFn()所指的绑定.如果你使用功能探索,你应该能够实现嘲弄otherFn().
// myModule.js
exports.otherFn = () => {
console.log('do something');
}
exports.testFn = () => {
exports.otherFn();
// do other things
}
Run Code Online (Sandbox Code Playgroud)
// myModule.test.js
import m from '../myModule';
m.otherFn = jest.fn();
Run Code Online (Sandbox Code Playgroud)
但正如@kentcdodds在之前的评论中所提到的,你可能不想嘲笑otherFn().相反,只需编写一个新的规范otherFn()并模拟它正在进行的任何必要的调用.
例如,如果otherFn()正在发出http请求...
// myModule.js
exports.otherFn = () => {
http.get('http://some-api.com', (res) => {
// handle stuff
});
};
Run Code Online (Sandbox Code Playgroud)
在这里,您可能希望根据模拟的http.get实现来模拟和更新断言.
// myModule.test.js
jest.mock('http', () => ({
get: jest.fn(() => {
console.log('test');
}),
}));
Run Code Online (Sandbox Code Playgroud)
我知道很久以前就有人问过这个问题,但我刚刚遇到了这种情况,最后找到了一个可行的解决方案。所以我想我会在这里分享。
对于模块:
// myModule.js
export function otherFn() {
console.log('do something');
}
export function testFn() {
otherFn();
// do other things
}
Run Code Online (Sandbox Code Playgroud)
您可以更改为以下内容:
// myModule.js
export const otherFn = () => {
console.log('do something');
}
export const testFn = () => {
otherFn();
// do other things
}
Run Code Online (Sandbox Code Playgroud)
将它们导出为常量而不是函数。我相信这个问题与 JavaScript 中的提升有关,使用const可以防止这种行为。
然后在您的测试中,您可以有以下内容:
import * as myModule from 'myModule';
describe('...', () => {
jest.spyOn(myModule, 'otherFn').mockReturnValue('what ever you want to return');
// or
myModule.otherFn = jest.fn(() => {
// your mock implementation
});
});
Run Code Online (Sandbox Code Playgroud)
你的模拟现在应该像你通常期望的那样工作。
根据Brian Adams 的回答,这就是我能够在 TypeScript 中使用相同方法的方式。此外,使用jest.doMock()可以仅在测试文件的某些特定测试中模拟模块函数,并为每个函数提供单独的模拟实现。
src/module.ts
import * as module from './module';
function foo(): string {
return `foo${module.bar()}`;
}
function bar(): string {
return 'bar';
}
export { foo, bar };
Run Code Online (Sandbox Code Playgroud)
测试/模块.test.ts
import { mockModulePartially } from './helpers';
import * as module from '../src/module';
const { foo } = module;
describe('test suite', () => {
beforeEach(function() {
jest.resetModules();
});
it('do not mock bar 1', async() => {
expect(foo()).toEqual('foobar');
});
it('mock bar', async() => {
mockModulePartially('../src/module', () => ({
bar: jest.fn().mockImplementation(() => 'BAR')
}));
const module = await import('../src/module');
const { foo } = module;
expect(foo()).toEqual('fooBAR');
});
it('do not mock bar 2', async() => {
expect(foo()).toEqual('foobar');
});
});
Run Code Online (Sandbox Code Playgroud)
测试/helpers.ts
export function mockModulePartially(
modulePath: string,
mocksCreator: (originalModule: any) => Record<string, any>
): void {
const testRelativePath = path.relative(path.dirname(expect.getState().testPath), __dirname);
const fixedModulePath = path.relative(testRelativePath, modulePath);
jest.doMock(fixedModulePath, () => {
const originalModule = jest.requireActual(fixedModulePath);
return { ...originalModule, ...mocksCreator(originalModule) };
});
}
Run Code Online (Sandbox Code Playgroud)
模块的模拟函数被移动到mockModulePartially位于单独文件中的辅助函数,以便可以从不同的测试文件中使用它(通常可以位于其他目录中)。它依赖于expect.getState().testPath修复被模拟的模块(modulePath)的路径(使其相对于helpers.ts包含mockModulePartially)。mocksCreator作为第二个参数传递的函数mockModulePartially应该返回模块的模拟。该函数接收originalModule并且模拟实现可以选择依赖它。
| 归档时间: |
|
| 查看次数: |
43636 次 |
| 最近记录: |