我创建了一个简单的倒计时组件,我想用Reaction测试库测试它是否正常工作.在浏览器中,一切都按预期工作,但在测试呈现的输出时,无论使用什么值,都不会前进超过一秒.
组件(基于Dan Abramovs blog post的自定义挂钩):
import React, { useEffect, useRef, useState } from "react"
import TimerButton from "../TimerButton/TimerButton"
// custom hook
function useInterval(callback, delay, state) {
const savedCallback = useRef()
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback
}, [callback])
// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current()
}
if (state.isOn) {
let id = setInterval(tick, delay)
return () => clearInterval(id)
}
}, [state])
}
const Timer = () => {
const initialState = {
minutes: 25,
seconds: 0,
isOn: false,
}
const [timerData, setTimerData] = useState(initialState)
useInterval(
() => {
if (timerData.seconds === 0) {
if (timerData.minutes === 0) {
setTimerData({ ...timerData, isOn: false })
} else {
setTimerData({
...timerData,
minutes: timerData.minutes - 1,
seconds: 59,
})
}
} else {
setTimerData({ ...timerData, seconds: timerData.seconds - 1 })
}
},
1000,
timerData,
)
const displayedTime = `${
timerData.minutes >= 10 ? timerData.minutes : `0${timerData.minutes}`
}:${timerData.seconds >= 10 ? timerData.seconds : `0${timerData.seconds}`}`
const startTimer = () => {
setTimerData({ ...timerData, isOn: true })
}
const stopTimer = () => {
setTimerData({ ...timerData, isOn: false })
}
const resetTimer = () => {
setTimerData(initialState)
}
return (
<div className="timer__container" data-testid="timerContainer">
<div className="timer__time-display" data-testid="timeDisplayContainer">
{displayedTime}
</div>
<div className="timer__button-wrap">
<TimerButton buttonAction={startTimer} buttonValue="Start" />
<TimerButton buttonAction={stopTimer} buttonValue="Stop" />
<TimerButton buttonAction={resetTimer} buttonValue="Reset" />
</div>
</div>
)
}
export default Timer
和测试(上面分别有一个beforeEach
和afterEach
调用jest.useFakeTimers()
和jest.useRealTimers()
):
it("Assert timer counts down by correct interval when starting timer", async () => {
render(<Timer />)
const initialTime = screen.getByText("25:00")
const startTimerButton = screen.getByText("Start")
expect(initialTime).toBeInTheDocument() // This works fine
fireEvent.click(startTimerButton)
act(() => {
jest.advanceTimersByTime(5000)
})
await waitFor(() => expect(screen.getByText("24:55")).toBeInTheDocument())
})
测试结果:
Unable to find an element with the text: 24:55. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
Ignored nodes: comments, script, style
<body>
<div>
<div
class="timer__container"
data-testid="timerContainer"
>
<div
class="timer__time-display"
data-testid="timeDisplayContainer"
>
24:59 <--- This value is always the same
</div>
<div
class="timer__button-wrap"
>
{ ... }
</div>
</div>
</body>
我想断言,在以不同的增量将时间向前移动后,将显示正确的时间,如果我可以做到这一点,那么它将允许进行更全面的测试.
我try 过使用Jest中的许多计时方法,包括AdvanceTimersByTime()、runOnlyPendingTimers()、runAllTimers(),但都没有用,我永远也无法获得超过1秒的时间.我还在useInterval
函数中的不同点添加了console.log()
,并可以验证该函数是否被调用了不同的次数,这取决于我try 将时间提前多少(它被称为Numbers of Second+1次,正如预期的那样).
在注销状态数据时,我看到除了最后一次运行之外,timerData
状态不会更新:
callback {"minutes":25,"seconds":0,"isOn":true} // Every time but the last
callback {"minutes":24,"seconds":59,"isOn":true} // The last time only
我认为这一定与我在更新期间如何引用状态值有关,或者与jest如何与状态更新交互假计时器有关,尽管目前我真的不知道.