Needless to say, the app is a lot more complicated that this, but the gist is the same. I cannot make major changes to the App, such as import/export the onSuccess function or changing to a class based component.
A button click kicks off the Login function, where I pass in an onSuccess function. I want to spy on the analyze function to make sure it is called, but in my test I have no way of calling the onSuccess function
Ideally I would like to do something like this:
test("analyze should be called", () => {
let analyzeSpy = jest.spyOn(Analytics, "analyze");
onSuccess() //<- cannot do this
expect(analyzeSpy).toHaveBeenCalledTimes(1);
});
Here is the app:
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;
This is Analytics.js:
export default {
analyze: () => {
console.log("Analysis done");
},
};
How can I test this?
Use render()
method of react-dom
module to render your component into document provided by js-dom
.
Use document.querySelector('button')
to get the button dom, dispatch a mouse click event.
Use jest.useFakeTimers()
to use fake versions of the standard timer functions( setTimeout
).
After dispatching the click event, use jest.advanceTimersByTime(2000)
to executes the macro task queue (tasks queued by setTimeout
).
Then, make an assertion.
Eg
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);
});
});
test result:
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.