我正在学习Jest和现代JavaScript,并试图测试一段代码。我的测试分组在一个描述()
容器中,我想在测试之间重置模拟(和模拟计数)。Stack Overflow上似乎有一些关于重置或清除模拟数据的问题,但它们似乎在我的案例中不起作用,而且我对JS来说太新了,无法理解其中的差异。
这是我的SUT(在source.js
):
module.exports = async function(delay) {
let query;
let ok;
for (let i = 0; i < 5; i++) {
query = context.functions.execute('getNextQuery');
if (query) {
ok = context.functions.execute('runQuery', query);
if (ok) {
context.functions.execute('markQueryAsRun', query.id);
}
} else {
break;
}
// Be nice to the API
await sleep(delay);
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
};
以下是测试:
const findAndRunQueries = require('./source');
describe('Some tests for findAndRunQueries', () => {
let globalMocks = {};
// context.functions.execute() becomes a mock
global.context = {
functions: {
execute: jest.fn((funcName, ...params) => {
// This calls a mock that we set up per test
return globalMocks[funcName](...params);
})
}
};
test('No queries need to be run', async () => {
// Don't need to mock other funcs if this one is falsey
setGlobalMock('getNextQuery', () => {
return null;
});
await findAndRunQueries(0);
// Ensure only getNextQuery is called
expect(getNthMockFunctionCall(0)[0]).toBe('getNextQuery');
expect(countMockFunctionCalls()).toBe(1);
});
test('One query needs to be run', async () => {
let callCount = 0;
// Return an object on the first run, null on the second
setGlobalMock('getNextQuery', () => {
if (callCount++ === 0) {
return {
something: 'fixme'
};
} else {
return null;
}
});
setGlobalMock('runQuery', (query) => {
return true;
});
setGlobalMock('markQueryAsRun', (queryId) => {
// Does not need to return anything
});
await findAndRunQueries(0);
// Ensure each func is called
expect(getNthMockFunctionCall(0)[0]).toBe('getNextQuery');
expect(getNthMockFunctionCall(1)[0]).toBe('runQuery');
expect(getNthMockFunctionCall(2)[0]).toBe('markQueryAsRun');
// We have a 4th call here, which returns null to terminate the loop early
expect(getNthMockFunctionCall(3)[0]).toBe('getNextQuery');
// Ensure there is no extra calls
expect(countMockFunctionCalls()).toBe(4);
});
function setGlobalMock(funcName, func) {
globalMocks[funcName] = func;
}
function getNthMockFunctionCall(n) {
return global.context.functions.execute.mock.calls[n];
}
function countMockFunctionCalls() {
return global.context.functions.execute.mock.calls.length;
}
});
这里有两个测试,不需要运行任何查询
和需要运行一个查询
。错误在第二个测试中,我怀疑正在发生的是模拟录制从第一个测试中持续出现,并破坏了结果。
我已经通过自己运行这个测试证实了这一点:
node node_modules/jest/bin/jest.js -t 'One query needs to be run'
但是,如果我同时运行两个测试,如下所示:
node node_modules/jest/bin/jest.js functions/findAndRunQueries
然后我就失败了:
● Some tests for findAndRunQueries › One query needs to be run
expect(received).toBe(expected) // Object.is equality
Expected: "runQuery"
Received: "getNextQuery"
53 | // Ensure each func is called
54 | expect(getNthMockFunctionCall(0)[0]).toBe('getNextQuery');
> 55 | expect(getNthMockFunctionCall(1)[0]).toBe('runQuery');
| ^
56 | expect(getNthMockFunctionCall(2)[0]).toBe('markQueryAsRun');
57 |
58 | // We have a 4th call here, which returns null to terminate the loop early
at Object.test (functions/findAndRunQueries/findAndRunQueries.test.js:55:46)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 0.637s, estimated 1s
Ran all test suites matching /functions\/findAndRunQueries/i.
它表示在第二个测试(getNthMockFunctionCall(1)[0]
)的第二个调用中的第一个参数是getNextQuery
,而我期望runQuery
。我认为这表明第一次测试的模拟结果是持久的。
我曾尝试添加以下内容:
beforeAll(() => {
jest.clearAllMocks();
});
我相信用玩笑的说法“清除”和“重置”是不同的——清除只是重置计数,重置也会移除模拟实现。所以我想要的是清晰的,但不幸的是这没有什么区别。
我还能尝试什么?
您可以尝试添加:
afterEach(() => {
jest.restoreAllMocks()
});
此外,最好在每次测试之前使用来定义重用的测试变量/模拟,以便为每次测试分别定义它们。