如何使用 Jest 和 Enzyme 测试 useEffect 中的函数?
How to test a function inside useEffect with Jest and Enzyme?
我正在使用 jest 和 enzyme 测试我的组件。我想在加载整个页面时加载我的组件,所以我在 useEffect
中使用加载事件,这是我的代码
const RatingsAndReviews = (props: RatingsAndReviewsProps) => {
const [pageLoaded, setPageLoaded] = useState<boolean>(false)
const handleLoad = () => {
if (document.readyState === 'complete') {
setTimeout(() => {
setPageLoaded(true)
}, 1500)
}
}
React.useEffect(() => {
window.addEventListener('load', handleLoad)
return () => {
window.removeEventListener('load', handleLoad)
}
}, [])
return (...some code)
}
我想测试这个 handleLoad
函数,但我无法弄清楚如何在我的组件中传递这个模拟函数,因为该组件已经期望 props 并且因为它是 TypeScript 我不能传递所需道具以外的任何其他内容,这是我的测试用例
it('Should run handleLoad function onMount', ()=>{
jest.spyOn(React, 'useEffect').mockImplementation(f => f())
const handleLoad = jest.fn();
wrapper = mount(<RatingsAndReviews {...propObj} />)
expect(handleLoad).toHaveBeenCalled();
})
我收到这个错误
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
尽量不要模拟third-party库函数的实现,因为不正确的模拟实现会破坏它的功能。例如,useEffect(effect)
react hook 不只是执行 effect
函数。考虑一下,
React defers running useEffect
until after the browser has painted.
你怎么嘲笑它?不正确的模拟会导致意想不到的行为。您的测试可能会基于不正确的模拟实现而通过,但代码在实际 运行 时会失败。这就是为什么你最好不要模拟 third-party 库。当然,这不是绝对的。如果 third-party 库的函数很简单并且 self-contained.
你可以模拟它们
对于 React 组件,我们应该做 black-box 测试,只测试组件的行为和功能,而不是实现。我们应该将组件视为一个单元而不是其中的功能。
我们应该测试 pageLoaded
状态改变时呈现的内容。
函数组件内部定义的事件处理程序是私有的,您不能从外部访问它们(测试代码)。所以你不能直接调用它们。相反,您应该通过用户事件触发它们。您的案例的 load
事件。
例如
index.tsx
:
import React from 'react';
import { useEffect, useState } from 'react';
export const RatingsAndReviews = (props) => {
const [pageLoaded, setPageLoaded] = useState<boolean>(false);
console.log('pageLoaded: ', pageLoaded);
const handleLoad = () => {
if (document.readyState === 'complete') {
setTimeout(() => {
setPageLoaded(true);
}, 1500);
}
};
useEffect(() => {
window.addEventListener('load', handleLoad);
return () => {
window.removeEventListener('load', handleLoad);
};
}, []);
return <div>{pageLoaded ? 'loaded' : 'not loaded'}</div>;
};
index.test.tsx
:
import { mount } from 'enzyme';
import React from 'react';
import { act } from 'react-dom/test-utils';
import { RatingsAndReviews } from '.';
describe('RatingsAndReviews', () => {
it('Should run handleLoad function onMount', () => {
jest.useFakeTimers();
const wrapper = mount(<RatingsAndReviews />);
window.dispatchEvent(new Event('load'));
expect(wrapper.text()).toBe('not loaded');
act(() => {
jest.advanceTimersByTime(1500);
});
expect(wrapper.text()).toBe('loaded');
});
});
测试结果:
PASS Whosebug/71953030/index.test.tsx (11.883 s)
RatingsAndReviews
✓ Should run handleLoad function onMount (47 ms)
console.log
pageLoaded: false
at RatingsAndReviews (Whosebug/71953030/index.tsx:7:11)
console.log
pageLoaded: true
at RatingsAndReviews (Whosebug/71953030/index.tsx:7:11)
-----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------|---------|----------|---------|---------|-------------------
All files | 93.33 | 75 | 80 | 92.86 |
index.tsx | 93.33 | 75 | 80 | 92.86 | 19
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 13.575 s, estimated 14 s
包版本:
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"jest": "^26.6.3",
我正在使用 jest 和 enzyme 测试我的组件。我想在加载整个页面时加载我的组件,所以我在 useEffect
中使用加载事件,这是我的代码
const RatingsAndReviews = (props: RatingsAndReviewsProps) => {
const [pageLoaded, setPageLoaded] = useState<boolean>(false)
const handleLoad = () => {
if (document.readyState === 'complete') {
setTimeout(() => {
setPageLoaded(true)
}, 1500)
}
}
React.useEffect(() => {
window.addEventListener('load', handleLoad)
return () => {
window.removeEventListener('load', handleLoad)
}
}, [])
return (...some code)
}
我想测试这个 handleLoad
函数,但我无法弄清楚如何在我的组件中传递这个模拟函数,因为该组件已经期望 props 并且因为它是 TypeScript 我不能传递所需道具以外的任何其他内容,这是我的测试用例
it('Should run handleLoad function onMount', ()=>{
jest.spyOn(React, 'useEffect').mockImplementation(f => f())
const handleLoad = jest.fn();
wrapper = mount(<RatingsAndReviews {...propObj} />)
expect(handleLoad).toHaveBeenCalled();
})
我收到这个错误
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
尽量不要模拟third-party库函数的实现,因为不正确的模拟实现会破坏它的功能。例如,useEffect(effect)
react hook 不只是执行 effect
函数。考虑一下,
React defers running
useEffect
until after the browser has painted.
你怎么嘲笑它?不正确的模拟会导致意想不到的行为。您的测试可能会基于不正确的模拟实现而通过,但代码在实际 运行 时会失败。这就是为什么你最好不要模拟 third-party 库。当然,这不是绝对的。如果 third-party 库的函数很简单并且 self-contained.
你可以模拟它们对于 React 组件,我们应该做 black-box 测试,只测试组件的行为和功能,而不是实现。我们应该将组件视为一个单元而不是其中的功能。
我们应该测试 pageLoaded
状态改变时呈现的内容。
函数组件内部定义的事件处理程序是私有的,您不能从外部访问它们(测试代码)。所以你不能直接调用它们。相反,您应该通过用户事件触发它们。您的案例的 load
事件。
例如
index.tsx
:
import React from 'react';
import { useEffect, useState } from 'react';
export const RatingsAndReviews = (props) => {
const [pageLoaded, setPageLoaded] = useState<boolean>(false);
console.log('pageLoaded: ', pageLoaded);
const handleLoad = () => {
if (document.readyState === 'complete') {
setTimeout(() => {
setPageLoaded(true);
}, 1500);
}
};
useEffect(() => {
window.addEventListener('load', handleLoad);
return () => {
window.removeEventListener('load', handleLoad);
};
}, []);
return <div>{pageLoaded ? 'loaded' : 'not loaded'}</div>;
};
index.test.tsx
:
import { mount } from 'enzyme';
import React from 'react';
import { act } from 'react-dom/test-utils';
import { RatingsAndReviews } from '.';
describe('RatingsAndReviews', () => {
it('Should run handleLoad function onMount', () => {
jest.useFakeTimers();
const wrapper = mount(<RatingsAndReviews />);
window.dispatchEvent(new Event('load'));
expect(wrapper.text()).toBe('not loaded');
act(() => {
jest.advanceTimersByTime(1500);
});
expect(wrapper.text()).toBe('loaded');
});
});
测试结果:
PASS Whosebug/71953030/index.test.tsx (11.883 s)
RatingsAndReviews
✓ Should run handleLoad function onMount (47 ms)
console.log
pageLoaded: false
at RatingsAndReviews (Whosebug/71953030/index.tsx:7:11)
console.log
pageLoaded: true
at RatingsAndReviews (Whosebug/71953030/index.tsx:7:11)
-----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------|---------|----------|---------|---------|-------------------
All files | 93.33 | 75 | 80 | 92.86 |
index.tsx | 93.33 | 75 | 80 | 92.86 | 19
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 13.575 s, estimated 14 s
包版本:
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"jest": "^26.6.3",