我正在测试以下文件
tests / client / blog.spec.js
import axios from 'axios';
import API_BASE from './config'; // '/api/v1/blog/'
const deletePost = id => {
console.log('ID: ', id);
axios.delete(`${API_BASE}/blog/${id}`, {
headers: { 'Content-type': 'application/json' },
data: null, // data null is necessary to pass the headers
})
.then((result) => {
console.log('AXIOS RESOLVED: ', result);
window.location.assign('/admin?cache=false');
console.log('CALLED window.location.assign with /admin?cache=false');
})
.catch((e) => {
console.log('AXIOS REJECTED: ', e);
window.location.assign('/admin?cache=true');
console.log('CALLED window.location.assign with /admin?cache=true');
});
};
const setupDeletePostHandler = () => {
const links = Array.prototype.slice.call(document.querySelectorAll('.delete-post'), 0);
if (links.length > 0) {
links.forEach(el => {
el.addEventListener('click', e => {
e.preventDefault();
e.currentTarget.style.pointerEvents = 'none';
e.currentTarget.querySelector('i').classList.remove('is-hidden');
const id = el.dataset.postId;
return deletePost(id);
});
});
}
};
const pageReady = page => {
switch (page) {
case 'admin-index':
setupDeletePostHandler();
break;
default:
break;
}
};
export default {
pageReady,
};
具有以下规格:
tests / client / blog.spec.js
import Blog from '../../src/client/js/blog.js';
import mockAxios from "axios";
jest.mock('axios');
describe('client/blog', () => {
beforeAll(() => {
jest.spyOn(window.location, 'assign').mockImplementation(() => {});
});
afterEach(() => {
mockAxios.delete.mockClear();
});
afterAll(() => {
window.location.assign.mockRestore();
});
it('set the DeletePostHandler', async function () {
// WHEN
const post = '<div class="posts"><div class="post">' +
'<p>Today should be a great day to be alive!</p>' +
'<div class="is-hidden">' +
'<a id="link_1" class="delete-post" href="/admin/edit-post/" data-post-id="">delete<i class="is-hidden"></i></a>' +
'</div></div>';
document.body.innerHTML = post;
Blog.pageReady('admin-index');
// WHEN
await document.querySelector('#link_1').click();
// THEN
expect(mockAxios.delete).toHaveBeenCalledTimes(1);
expect(window.location.assign).toHaveBeenCalled();
console.log('CALLS: ', window.location.assign.mock.calls);
expect(window.location.assign).toHaveBeenCalledWith('/admin?cache=false');
});
});
失败,这是控制台:
__ mocks __ / axios.js
export default {
delete: jest.fn((url) => {
if (url === '/api/v1/blog/1') {
return Promise.resolve({
data: {},
status: 200,
statusText: 'OK',
headers: {}
});
} else {
return Promise.reject({
data: {},
status: 400,
statusText: 'Error',
headers: {}
});
}
})
};
console.log
$ yarn test-client
yarn run v1.9.4
$ jest tests/client/*.js
FAIL tests/client/blog.spec.js
client/blog
✕ set the DeletePostHandler (47ms)
● client/blog › set the DeletePostHandler
expect(jest.fn()).toHaveBeenCalled()
Expected mock function to have been called, but it was not called.
33 | // THEN
34 | expect(mockAxios.delete).toHaveBeenCalledTimes(1);
> 35 | expect(window.location.assign).toHaveBeenCalled();
| ^
36 | console.log('CALLS: ', window.location.assign.mock.calls);
37 | expect(window.location.assign).toHaveBeenCalledWith('/admin?cache=false');
38 | });
at Object.toHaveBeenCalled (tests/client/blog.spec.js:35:36)
at tryCatch (node_modules/regenerator-runtime/runtime.js:62:40)
at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:296:22)
at Generator.prototype.(anonymous function) [as next] (node_modules/regenerator-runtime/runtime.js:114:21)
at step (tests/client/blog.spec.js:22:191)
at tests/client/blog.spec.js:22:361
console.log src/client/js/blog.js:7
ID:
console.log src/client/js/blog.js:18
AXIOS REJECTED: { data: {}, status: 400, statusText: 'Error', headers: {} }
console.log src/client/js/blog.js:20
CALLED window.location.assign with /admin?cache=true
奇怪...日志显示,在Axios请求被拒绝后,应该调用模拟函数...并非如此
注意:当我在解决Axios请求的情况下测试脚本时,正确调用了window.location.assign模拟...
答案 0 :(得分:1)
过去我也遇到过类似的问题。
catch
在expect(window.location.assign).toHaveBeenCalled()
运行并失败时尚未执行。
click()
实际上不返回任何内容,因此await
没有什么可等待的。
根据我的经验,调用await
可以使PromiseJobs
队列有一个循环,这就是then
运行并在Axios请求解决后测试通过的原因。
catch
似乎要花费两个周期的PromiseJobs
队列,因此它没有被测试执行,直到await
为止,并在断言时失败。
解决方案是在断言之前确保catch
已运行。
理想情况下,这是通过返回Promise
和await
并将其放入测试中来完成的,就像您尝试这样做一样。此测试的棘手部分是click
实际上没有返回任何内容,因此没有Promise
可以等待。
在这种情况下,无法await
实际的Promise
是一个好的解决方法,就是await
已解决Promise
所需的周期数PromiseJobs
队列。
每次Promise
被await
处理时,其余的测试实际上都在PromiseJobs
的后面排队,并允许队列中已有的任何内容在继续测试之前运行。
在这种情况下,等待PromiseJobs
队列的两个周期将使catch
有运行的机会:
document.querySelector('#link_1').click(); // remove "await" since nothing is returned
await Promise.resolve().then(); // wait two cycles of the PromiseJobs queue
// THEN
expect(mockAxios.delete).toHaveBeenCalledTimes(1);
expect(window.location.assign).toHaveBeenCalled(); // SUCCESS