不用说,该应用程序比这复杂得多,但要点是相同的。我无法对应用程序进行重大更改,例如导入/导出 onSuccess 函数或更改为基于类的组件。
单击按钮会启动登录功能,我在其中传递了一个 onSuccess 功能。我想监视分析函数以确保它被调用,但在我的测试中我无法调用 onSuccess 函数
理想情况下,我想做这样的事情:
test("analyze should be called", () => {
let analyzeSpy = jest.spyOn(Analytics, "analyze");
onSuccess() //<- cannot do this
expect(analyzeSpy).toHaveBeenCalledTimes(1);
});
这是应用程序:
import Analytics from "./Analytics";
export function Login({ onLoginSuccess }) {
setTimeout(function () {
console.log("TIMEOUT OVER");
onLoginSuccess();
}, 2000);
}
function App() {
function handleClick() {
console.log("Login");
Login({
onLoginSuccess: onSuccess,
});
}
function onSuccess() {
console.log("Login success");
Analytics.analyze();
}
return (
<>
<button
onClick={() => {
handleClick();
}}
>
Login
</button>
</>
);
}
export default App;
这是 Analytics.js:
export default {
analyze: () => {
console.log("Analysis done");
},
};
我该如何测试?
答案 0 :(得分:2)
使用 render()
模块的 react-dom
方法将您的组件渲染到 js-dom
提供的文档中。
使用 document.querySelector('button')
获取按钮 dom,调度鼠标点击事件。
使用 jest.useFakeTimers()
来使用标准计时器函数 (setTimeout
) 的伪造版本。
分派点击事件后,使用jest.advanceTimersByTime(2000)
执行宏任务队列(由setTimeout
排队的任务)。
然后,做出断言。
例如
App.jsx
:
import React from 'react';
import Analytics from './Analytics';
export function Login({ onLoginSuccess }) {
setTimeout(function () {
console.log('TIMEOUT OVER');
onLoginSuccess();
}, 2000);
}
function App() {
function handleClick() {
console.log('Login');
Login({
onLoginSuccess: onSuccess,
});
}
function onSuccess() {
console.log('Login success');
Analytics.analyze();
}
return (
<>
<button
onClick={() => {
handleClick();
}}
>
Login
</button>
</>
);
}
export default App;
App.test.jsx
:
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { act } from 'react-dom/test-utils';
import App from './App';
import Analytics from './Analytics';
describe('68400320', () => {
let container = null;
beforeEach(() => {
// setup a DOM element as a render target
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
// cleanup on exiting
unmountComponentAtNode(container);
container.remove();
container = null;
});
test('should pass', () => {
const analyzeSpy = jest.spyOn(Analytics, 'analyze');
jest.useFakeTimers();
act(() => {
render(<App />, container);
});
const button = document.querySelector('button');
act(() => {
button?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
jest.advanceTimersByTime(2000);
});
expect(analyzeSpy).toBeCalledTimes(1);
});
});
测试结果:
PASS examples/68400320/App.test.jsx (8.12 s)
68400320
✓ should pass (52 ms)
console.log
Login
at handleClick (examples/68400320/App.jsx:13:13)
console.log
TIMEOUT OVER
at examples/68400320/App.jsx:6:13
console.log
Login success
at onSuccess (examples/68400320/App.jsx:19:13)
console.log
Analysis done
at Object.analyze (examples/68400320/Analytics.js:3:13)
--------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
Analytics.js | 100 | 100 | 100 | 100 |
App.jsx | 100 | 100 | 100 | 100 |
--------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 8.666 s, estimated 10 s
Ran all test suites related to changed files.