React Jest setInterval 测试覆盖率
Posted
技术标签:
【中文标题】React Jest setInterval 测试覆盖率【英文标题】:React Jest setInterval test coverage 【发布时间】:2020-06-03 22:10:49 【问题描述】:我正在尝试对功能组件进行一些测试覆盖。我在 useEffect 中有一个 setInterval 可以每隔几秒更改一条消息。对于我的测试似乎我没有测试 useEffect 中的任何内容。我试过jest.useFakeTimers();
我也在设置 useState。
组件:
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import styled from "styled-components";
// Array of strings
import messages from "./messages";
export const Wrapper = styled.div`
font-family: 'Lato', sans-serif !important;
box-sizing: border-box;
width: 100%;
display: block;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
height: 100vh;
h1
font-size: 2rem;
color: rgb(100, 100, 100);
;
`;
const loadMessage = () =>
// this is the line that is not being tested.
return messages[Math.floor(Math.random() * messages.length)];
;
export const Loader = () =>
const [message, setMessage] = useState("Loading...");
useEffect(() =>
const id = setInterval(() =>
// this is the line that is not being tested.
setMessage(loadMessage());
, 3000);
return () => clearInterval(id);
, [message]);
return (
<Wrapper id="login-content">
<Row className="align-items-center h-100">
<Col className="col-md-12 mx-auto">
<div className="container text-center">
<h1 className="loader" data-testid="loader" aria-live="polite">
message
</h1>
</div>
</Col>
</Row>
</Wrapper>
);
;
测试:
import React from "react";
import shallow from "enzyme";
import Loader from "./Loader";
const doAsync = c =>
setTimeout(() =>
c(true);
, 3000);
;
jest.useFakeTimers();
describe("Loader component", () =>
it("tests state", () =>
const wrapper = shallow(<Loader />);
const message = wrapper.find(".loader").text();
jest.advanceTimersByTime(3000);
const callback1 = () =>
expect(wrapper.find(".loader").text()).not.toMatch(message);
;
doAsync(callback1);
jest.useRealTimers();
);
);
【问题讨论】:
是你expect
不一定执行的问题吗?如果是这样,您可能希望使用it("tests state", (done) => ...
,然后在您的expect
语句之后但仍在callback1
中,调用done();
。另外,我对advanceTimersByTime
不熟悉,但如果您关心这一点,我希望您在拨打doAsync
之后再拨打它;不过不确定。
是的,我的期望没有执行。 TBH,我只是想尝试一种测试 useState 的好方法。
您要测试useState
或useEffect
?还是两者兼而有之?
@WillCain 两者。我的测试覆盖率专门要求 setMessage 变量,但如果可能的话,我想测试两者。
【参考方案1】:
我偶然发现了酶问题:https://github.com/airbnb/enzyme/issues/2086
useEffect 函数在组件运行时似乎没有执行 用浅层渲染。
鉴于此,我要做的是模拟 UseEffect。
const doAsync = c =>
setTimeout(() =>
c(true);
, 3000);
;
jest.useFakeTimers();
describe("Loader component", () =>
beforeEach(() =>
const useEffect = jest
.spyOn(React, "useEffect")
.mockImplementation(f => f());
);
it("tests state", done =>
const wrapper = shallow(<Loader />);
const message = wrapper.find(".loader").text();
jest.advanceTimersByTime(3000);
const callback1 = () =>
expect(wrapper.find(".loader").text()).not.toMatch(message);
;
doAsync(callback1);
jest.useRealTimers();
done();
);
);
这使我的测试通过并获得 100% 的覆盖率。
【讨论】:
不错的发现。顺便说一句,不确定完整(非浅层)渲染是否可以接受,但如果可以,那也许是另一种选择。另外,我不确定您的expect
是否会在不将done()
移动到callback1
中的情况下可靠地执行,但当然您可以使用不正确的expect
或console.log
或调试器来验证这一点。
我按照这个例子进行了测试,测试通过了,但我放了一些日志,我希望有两个重复但只显示一次。然后以一种本应失败但仍然通过的方式更改了我的测试... Idk 为什么添加超时会使测试通过,但绝对不是正确的答案以上是关于React Jest setInterval 测试覆盖率的主要内容,如果未能解决你的问题,请参考以下文章
意外的导入令牌 - 使用 Jest 测试 React Native
React - Jest - 测试 preventDefault Action