我有一个集成测试的 Jest 测试套件,使用 suite-global 模拟进行数据库访问,其中根据 SQL 我返回不同的模拟响应:
jest.mock('@my-org/our-mysql-wrapper', () => {
const query = jest.fn(async (sql, params) => {
if (sql === 'select foo from bar') {
return [];
} else if (sql === 'select baz') {
return [{ messageId: 3 }, { messageId: 4 }, { messageId: 5 }];
} else if (...) {
return ...;
} else {
console.log('UNEXPECTED QUERY SENT TO MOCK: ', sql, params);
return [];
}
});
const end = jest.fn(async () => true);
return jest.fn(() => ({ query, end }));
});
describe('suite', () => {
//tests here
});
这对阳性测试很有用,但我对它感到沮丧的是阴性测试。例如,在某些情况下,如果数据库不返回任何结果,我们可能想要抛出错误。为了测试我需要让我的模拟对于相同的输入有不同的表现。典型的正面测试不会在运行前覆盖数据库模拟,而负面测试需要:
it('should throw and handle an error if the db returns no results for Widget lookup', async () => {
const mockDB = require('@my-org/our-mysql-wrapper')();
mockDB.query.mockImplementation(jest.fn(asnyc (sql, params) => {
if ( sql === 'select * from Widgets' ){
//this is the use-case that I want to override for this test
return [];
}else{
//...
}
}));
const someValue = await tool.doThing();
expect(buglogger).toHaveBeenCalled(); //actual test will be more specific...
//I tried plugging in mockRestore/mockClear/mockReset here
});
正如上面所写的,这个测试实际上通过了,但它会破坏在它之后运行的测试,因为它不会自行清理。据我所知,这就是 mockClear()
、mockReset()
和 mockRestore()
在不同变体中应该做的事情;但我一直无法找到一种方法,在测试结束时将我的模拟恢复到原始的预覆盖模拟实现。
我还在其他一些情况下使用了 jest.spyOn()
,但这也不是我想要的。在这种情况下,我的测试失败了,其他测试的模拟也仍然损坏。
it('should throw and handle an error if the db returns no results for Widget lookup', async () => {
const mockDB = require('@my-org/our-mysql-wrapper')();
jest.spyOn(mockDb, 'query');
mockDB.query.mockImplementation(jest.fn(asnyc (sql, params) => {
if ( sql === 'select * from Widgets' ){
//this is the use-case that I want to override for this test
return [];
}else{
//...
}
}));
const someValue = await tool.doThing();
expect(buglogger).toHaveBeenCalled(); //actual test will be more specific...
mockDb.query.mockRestore();
});
我也尝试过 mockImplementationOnce()
并且这对我不起作用,因为有问题的查询不是将运行的第一个查询。使用此方法确实会在其自身之后自动清理,但不会(据我所知不能)使我的测试通过,因为它在调用相关查询之前第一次使用后会自行清理。
但是既然 mockImplementationOnce
可以通过一种恢复原始模拟的方式清理自己,难道不应该有一些手动方法来覆盖现有的模拟只是为了一个测试吗?这就是 mockImplementationOnce
正在做的,不是吗? (在第一次调用后清理而不是在 1 次测试后清理;但它似乎正在恢复原始模拟......)
我在这里做错了什么?
答案 0 :(得分:0)
我认为问题出在这两种情况下,您正在尝试将模拟分配给您导入的实际包。
您是否尝试过如下更改模拟设置:
const mockDB = require('@my-org/our-mysql-wrapper')();
const dbSpy = jest.spyOn(mockDB, 'query');
dbSpy.mockImplementation(jest.fn(asnyc (sql, params) => {
...
}));
dbSpy.mockClear();
dbSpy.mockRestore();