Javascript 如何在单个测试的基础上更改模拟实现[Jestjs]
我想在每个单一测试的基础上更改模拟依赖项的实现,方法是扩展默认模拟的行为,并在下一个测试执行时将其恢复到原始实现 更简单地说,这就是我试图实现的目标:Javascript 如何在单个测试的基础上更改模拟实现[Jestjs],javascript,unit-testing,mocking,jestjs,Javascript,Unit Testing,Mocking,Jestjs,我想在每个单一测试的基础上更改模拟依赖项的实现,方法是扩展默认模拟的行为,并在下一个测试执行时将其恢复到原始实现 更简单地说,这就是我试图实现的目标: 模拟依赖项 在单个测试中更改/扩展模拟实现 在执行下一个测试时恢复到原始模拟 我目前正在使用Jest v21 下面是典型的Jest测试的样子: \uuuu mocks\uuuu/myModule.js const myMockedModule = jest.genMockFromModule('../myModule'); myMockedMo
Jest v21
下面是典型的Jest测试的样子:
\uuuu mocks\uuuu/myModule.js
const myMockedModule = jest.genMockFromModule('../myModule');
myMockedModule.a = jest.fn(() => true);
myMockedModule.b = jest.fn(() => true);
export default myMockedModule;
import myMockedModule from '../myModule';
// Mock myModule
jest.mock('../myModule');
beforeEach(() => {
jest.clearAllMocks();
});
describe('MyTest', () => {
it('should test with default mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
it('should override myMockedModule.b mock result (and leave the other methods untouched)', () => {
// Extend change mock
myMockedModule.a(); // === true
myMockedModule.b(); // === 'overridden'
// Restore mock to original implementation with no side effects
});
it('should revert back to default myMockedModule mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
});
const myMockedModule = jest.genMockFromModule('../myModule');
let a = true;
let b = true;
myMockedModule.a = jest.fn(() => a);
myMockedModule.b = jest.fn(() => b);
myMockedModule.__setA = (value) => { a = value };
myMockedModule.__setB = (value) => { b = value };
myMockedModule.__reset = () => {
a = true;
b = true;
};
export default myMockedModule;
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
myModule.__setB('overridden');
myModule.a(); // === true
myModule.b(); // === 'overridden'
myModule.__reset();
});
\uuuu tests\uuuu/myTest.js
const myMockedModule = jest.genMockFromModule('../myModule');
myMockedModule.a = jest.fn(() => true);
myMockedModule.b = jest.fn(() => true);
export default myMockedModule;
import myMockedModule from '../myModule';
// Mock myModule
jest.mock('../myModule');
beforeEach(() => {
jest.clearAllMocks();
});
describe('MyTest', () => {
it('should test with default mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
it('should override myMockedModule.b mock result (and leave the other methods untouched)', () => {
// Extend change mock
myMockedModule.a(); // === true
myMockedModule.b(); // === 'overridden'
// Restore mock to original implementation with no side effects
});
it('should revert back to default myMockedModule mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
});
const myMockedModule = jest.genMockFromModule('../myModule');
let a = true;
let b = true;
myMockedModule.a = jest.fn(() => a);
myMockedModule.b = jest.fn(() => b);
myMockedModule.__setA = (value) => { a = value };
myMockedModule.__setB = (value) => { b = value };
myMockedModule.__reset = () => {
a = true;
b = true;
};
export default myMockedModule;
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
myModule.__setB('overridden');
myModule.a(); // === true
myModule.b(); // === 'overridden'
myModule.__reset();
});
以下是我到目前为止所做的尝试:
1 - 专业人士
- 在第一次调用后恢复到原始实现
- 如果测试多次调用
,它将中断b
- 在未调用
之前,它不会恢复到原始实现(在下一个测试中泄漏)b
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
myMockedModule.b.mockImplementationOnce(() => 'overridden');
myModule.a(); // === true
myModule.b(); // === 'overridden'
});
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
jest.doMock('../myModule', () => {
return {
a: jest.fn(() => true,
b: jest.fn(() => 'overridden',
}
});
myModule.a(); // === true
myModule.b(); // === 'overridden'
});
beforeEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
});
// Mock myModule
jest.mock('../myModule');
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
const spy = jest.spyOn(myMockedModule, 'b').mockImplementation(() => 'overridden');
myMockedModule.a(); // === true
myMockedModule.b(); // === 'overridden'
// How to get back to original mocked value?
});
2 - 专业人士
- 在每个测试中显式地重新模拟
- 无法为所有测试定义默认模拟实现
- 无法扩展默认实现,强制重新声明每个模拟 方法
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
myMockedModule.b.mockImplementationOnce(() => 'overridden');
myModule.a(); // === true
myModule.b(); // === 'overridden'
});
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
jest.doMock('../myModule', () => {
return {
a: jest.fn(() => true,
b: jest.fn(() => 'overridden',
}
});
myModule.a(); // === true
myModule.b(); // === 'overridden'
});
beforeEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
});
// Mock myModule
jest.mock('../myModule');
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
const spy = jest.spyOn(myMockedModule, 'b').mockImplementation(() => 'overridden');
myMockedModule.a(); // === true
myMockedModule.b(); // === 'overridden'
// How to get back to original mocked value?
});
3-使用setter方法进行手动模拟(如所述) 专业人士
- 完全控制模拟结果
- 大量样板代码
- 难以长期维持
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
myMockedModule.b.mockImplementationOnce(() => 'overridden');
myModule.a(); // === true
myModule.b(); // === 'overridden'
});
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
jest.doMock('../myModule', () => {
return {
a: jest.fn(() => true,
b: jest.fn(() => 'overridden',
}
});
myModule.a(); // === true
myModule.b(); // === 'overridden'
});
beforeEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
});
// Mock myModule
jest.mock('../myModule');
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
const spy = jest.spyOn(myMockedModule, 'b').mockImplementation(() => 'overridden');
myMockedModule.a(); // === true
myMockedModule.b(); // === 'overridden'
// How to get back to original mocked value?
});
\uuuu mocks\uuuu/myModule.js
const myMockedModule = jest.genMockFromModule('../myModule');
myMockedModule.a = jest.fn(() => true);
myMockedModule.b = jest.fn(() => true);
export default myMockedModule;
import myMockedModule from '../myModule';
// Mock myModule
jest.mock('../myModule');
beforeEach(() => {
jest.clearAllMocks();
});
describe('MyTest', () => {
it('should test with default mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
it('should override myMockedModule.b mock result (and leave the other methods untouched)', () => {
// Extend change mock
myMockedModule.a(); // === true
myMockedModule.b(); // === 'overridden'
// Restore mock to original implementation with no side effects
});
it('should revert back to default myMockedModule mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
});
const myMockedModule = jest.genMockFromModule('../myModule');
let a = true;
let b = true;
myMockedModule.a = jest.fn(() => a);
myMockedModule.b = jest.fn(() => b);
myMockedModule.__setA = (value) => { a = value };
myMockedModule.__setB = (value) => { b = value };
myMockedModule.__reset = () => {
a = true;
b = true;
};
export default myMockedModule;
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
myModule.__setB('overridden');
myModule.a(); // === true
myModule.b(); // === 'overridden'
myModule.__reset();
});
\uuuu tests\uuuu/myTest.js
const myMockedModule = jest.genMockFromModule('../myModule');
myMockedModule.a = jest.fn(() => true);
myMockedModule.b = jest.fn(() => true);
export default myMockedModule;
import myMockedModule from '../myModule';
// Mock myModule
jest.mock('../myModule');
beforeEach(() => {
jest.clearAllMocks();
});
describe('MyTest', () => {
it('should test with default mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
it('should override myMockedModule.b mock result (and leave the other methods untouched)', () => {
// Extend change mock
myMockedModule.a(); // === true
myMockedModule.b(); // === 'overridden'
// Restore mock to original implementation with no side effects
});
it('should revert back to default myMockedModule mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
});
const myMockedModule = jest.genMockFromModule('../myModule');
let a = true;
let b = true;
myMockedModule.a = jest.fn(() => a);
myMockedModule.b = jest.fn(() => b);
myMockedModule.__setA = (value) => { a = value };
myMockedModule.__setB = (value) => { b = value };
myMockedModule.__reset = () => {
a = true;
b = true;
};
export default myMockedModule;
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
myModule.__setB('overridden');
myModule.a(); // === true
myModule.b(); // === 'overridden'
myModule.__reset();
});
4 - 缺点
- 我无法将
恢复为原始模拟返回值,因此会影响下一个测试mockImplementation
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
myMockedModule.b.mockImplementationOnce(() => 'overridden');
myModule.a(); // === true
myModule.b(); // === 'overridden'
});
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
jest.doMock('../myModule', () => {
return {
a: jest.fn(() => true,
b: jest.fn(() => 'overridden',
}
});
myModule.a(); // === true
myModule.b(); // === 'overridden'
});
beforeEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
});
// Mock myModule
jest.mock('../myModule');
it('should override myModule.b mock result (and leave the other methods untouched)', () => {
const spy = jest.spyOn(myMockedModule, 'b').mockImplementation(() => 'overridden');
myMockedModule.a(); // === true
myMockedModule.b(); // === 'overridden'
// How to get back to original mocked value?
});
编写测试的一个好模式是创建一个setupfactory函数,该函数返回测试当前模块所需的数据 下面是第二个示例之后的一些示例代码,尽管它允许以可重用的方式提供默认值和重写值
constspyreturns=returnValue=>jest.fn(()=>returnValue);
描述(“场景”,()=>{
常量设置=(模拟覆盖)=>{
常量mockedFunctions={
答:spyReturns(正确),
b:spyReturns(正确),
…模拟覆盖
}
返回{
mockedModule:jest.doMock('../myModule',()=>mockedmfunctions)
}
}
它(“模块a应返回true”,()=>{
常量{mockedModule}=setup();
expect(mockedModule.a()).toEqual(true)
});
它(“应返回模块a的覆盖”,()=>{
常量预期值=“覆盖”
const{mockedModule}=setup({a:spyReturns(预期值)});
expect(mockedModule.a()).toEqual(预期值)
});
});
使用
import{funcToMock}from./somewhere';
//从“./某处”导入{(funcToMock as jest.Mock)};//如果使用TypeScript
开玩笑。嘲弄(“/某处”);
在每个之前(()=>{
mockImplementation(()=>{/*默认实现*/});
});
test('需要不同funcToMock实现的案例',()=>{
mockImplementation(()=>{/*特定于此测试的实现*/});
// ...
});
派对有点晚,但如果其他人对此有问题
我们使用TypeScript、ES6和babel进行react本地开发
我们通常在根目录下模拟外部NPM模块
我想覆盖aws amplify的Auth类中模块的特定函数,以进行特定测试
import{Auth}来自“aws放大”;
从“/GetJwtToken”导入GetJwtToken;
...
它('当idToken应该返回“123”时,异步()=>{
const spy=jest.spyOn(Auth,'currentSession').mockImplementation(()=>({
getIdToken:()=>({
getJwtToken:()=>“123”,
}),
}));
const result=wait GetJwtToken();
期望(结果)。托比('123');
spy.mockRestore();
});
要点:
教程:
不错。但是对于像“@private repo/module”这样的npm模块,您如何执行选项2?我看到的大多数例子都有相对路径?这对已安装的模块也有效吗?这是唯一对我有效的方法,用最少的样板文件。在我的场景中,我从一个没有默认导出的包中获得了一个名为TypeScript的导出,因此我最终使用
import*作为MyModule
然后const{useQuery}=MyModule
这样我仍然可以以相同的方式使用导入,而不必到处执行MyModule.someExport
。