尽管已经包裹,但没有包裹在行为错误中
Getting not wrapped in act error despite already wrapped
我正在测试我的 React 组件以验证由 setTimeout
延迟的回调函数行为,但我收到以下错误,尽管我的假计时器已经在 act
块中触发。
错误如下:
C:\dev\node\node.exe --require "C:\dev\JetBrains\IntelliJ IDEA 2021.1.1\plugins\JavaScriptLanguage\helpers\jest-intellij\lib\jest-intellij-stdin-fix.js" C:\Users\Reph0\Desktop\my-app\node_modules\react-scripts\bin\react-scripts.js test --colors --reporters "C:\dev\JetBrains\IntelliJ IDEA 2021.1.1\plugins\JavaScriptLanguage\helpers\jest-intellij\lib\jest-intellij-reporter.js" --verbose "--testNamePattern=^Timeout component should randomize value until more than 0\.9$" --runTestsByPath C:/Users/Reph0/Desktop/my-app/src/TimeoutComponent.spec.tsx
console.error
Warning: An update to TimeoutComponent inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
at TimeoutComponent (C:\Users\Reph0\Desktop\my-app\src\TimeoutComponent.tsx:8:35)
组件
import * as React from "react";
interface Props {
getValue: () => Promise<number>;
}
const TimeoutComponent: React.FC<Props> = (props) => {
const [value, setValue] = React.useState<number>(0);
React.useEffect(() => {
if (value < 0.9) {
const timeoutId = setTimeout(() => props.getValue().then(setValue), 300);
return () => {
clearTimeout(timeoutId);
};
}
}, [value]);
return (
<>
<p>
Value: <span id="value">{value}</span>
</p>
</>
);
};
export default TimeoutComponent;
测试
import { render } from "@testing-library/react";
import React from "react";
import { act } from "react-dom/test-utils";
import TimeoutComponent from "./TimeoutComponent";
const getValue = jest.fn();
describe("Timeout component", () => {
beforeEach(() => {
jest.resetAllMocks();
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
it("should randomize value until more than 0.9", () => {
getValue
.mockResolvedValueOnce(0.1)
.mockResolvedValueOnce(0.2)
.mockResolvedValueOnce(0.3)
.mockResolvedValueOnce(0.4)
.mockResolvedValueOnce(0.7)
.mockResolvedValueOnce(0.92)
.mockResolvedValueOnce(0.6);
render(<TimeoutComponent getValue={getValue} />);
act(() => {
jest.runAllTimers();
});
expect(getValue).toBeCalledTimes(6);
});
});
这是我的 package.json
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"@types/jest": "^26.0.15",
"@types/node": "^12.0.0",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
"typescript": "^4.1.2",
"web-vitals": "^1.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
这是我的 codesandbox 的 link,但似乎 jest.runAllTimers()
在那里工作得不好。
如果有人能伸出援手,我将不胜感激:)
错误发生是因为测试期间状态正在改变。我们可以等待状态更改,然后再进行断言。
对于你的情况,我们可以等待不包含0.6的内容(0.92之后状态不会改变),判断之前是否调用过getValue6 times
。
it("should randomize value until more than 0.9", async () => {
getValue
.mockResolvedValueOnce(0.1)
.mockResolvedValueOnce(0.2)
.mockResolvedValueOnce(0.3)
.mockResolvedValueOnce(0.4)
.mockResolvedValueOnce(0.7)
.mockResolvedValueOnce(0.92)
.mockResolvedValueOnce(0.6);
const result = render(<TimeoutComponent getValue={getValue} />);
try {
jest.runAllTimers();
// wait until the content contains 0.6
// will timeout as 0.6 will never be displayed; and
// will go to catch and finally block
await waitFor(() => {
result.getByText(/0.6/)
})
} catch (e) {
console.log("timeout for waiting for 0.6 appear: ", e.message)
} finally {
// get the latest value in the content before timeout error, must be 0.92
expect(result.container.getElementsByTagName("span")[0].innerHTML).toEqual("0.92");
// get the number of time getValue has been called before timeout error, must be 6
expect(getValue).toBeCalledTimes(6);
}
});
参考waitFor
:
https://testing-library.com/docs/guide-disappearance/#waiting-for-appearance
我正在测试我的 React 组件以验证由 setTimeout
延迟的回调函数行为,但我收到以下错误,尽管我的假计时器已经在 act
块中触发。
错误如下:
C:\dev\node\node.exe --require "C:\dev\JetBrains\IntelliJ IDEA 2021.1.1\plugins\JavaScriptLanguage\helpers\jest-intellij\lib\jest-intellij-stdin-fix.js" C:\Users\Reph0\Desktop\my-app\node_modules\react-scripts\bin\react-scripts.js test --colors --reporters "C:\dev\JetBrains\IntelliJ IDEA 2021.1.1\plugins\JavaScriptLanguage\helpers\jest-intellij\lib\jest-intellij-reporter.js" --verbose "--testNamePattern=^Timeout component should randomize value until more than 0\.9$" --runTestsByPath C:/Users/Reph0/Desktop/my-app/src/TimeoutComponent.spec.tsx
console.error
Warning: An update to TimeoutComponent inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
at TimeoutComponent (C:\Users\Reph0\Desktop\my-app\src\TimeoutComponent.tsx:8:35)
组件
import * as React from "react";
interface Props {
getValue: () => Promise<number>;
}
const TimeoutComponent: React.FC<Props> = (props) => {
const [value, setValue] = React.useState<number>(0);
React.useEffect(() => {
if (value < 0.9) {
const timeoutId = setTimeout(() => props.getValue().then(setValue), 300);
return () => {
clearTimeout(timeoutId);
};
}
}, [value]);
return (
<>
<p>
Value: <span id="value">{value}</span>
</p>
</>
);
};
export default TimeoutComponent;
测试
import { render } from "@testing-library/react";
import React from "react";
import { act } from "react-dom/test-utils";
import TimeoutComponent from "./TimeoutComponent";
const getValue = jest.fn();
describe("Timeout component", () => {
beforeEach(() => {
jest.resetAllMocks();
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
it("should randomize value until more than 0.9", () => {
getValue
.mockResolvedValueOnce(0.1)
.mockResolvedValueOnce(0.2)
.mockResolvedValueOnce(0.3)
.mockResolvedValueOnce(0.4)
.mockResolvedValueOnce(0.7)
.mockResolvedValueOnce(0.92)
.mockResolvedValueOnce(0.6);
render(<TimeoutComponent getValue={getValue} />);
act(() => {
jest.runAllTimers();
});
expect(getValue).toBeCalledTimes(6);
});
});
这是我的 package.json
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"@types/jest": "^26.0.15",
"@types/node": "^12.0.0",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
"typescript": "^4.1.2",
"web-vitals": "^1.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
这是我的 codesandbox 的 link,但似乎 jest.runAllTimers()
在那里工作得不好。
如果有人能伸出援手,我将不胜感激:)
错误发生是因为测试期间状态正在改变。我们可以等待状态更改,然后再进行断言。
对于你的情况,我们可以等待不包含0.6的内容(0.92之后状态不会改变),判断之前是否调用过getValue6 times
。
it("should randomize value until more than 0.9", async () => {
getValue
.mockResolvedValueOnce(0.1)
.mockResolvedValueOnce(0.2)
.mockResolvedValueOnce(0.3)
.mockResolvedValueOnce(0.4)
.mockResolvedValueOnce(0.7)
.mockResolvedValueOnce(0.92)
.mockResolvedValueOnce(0.6);
const result = render(<TimeoutComponent getValue={getValue} />);
try {
jest.runAllTimers();
// wait until the content contains 0.6
// will timeout as 0.6 will never be displayed; and
// will go to catch and finally block
await waitFor(() => {
result.getByText(/0.6/)
})
} catch (e) {
console.log("timeout for waiting for 0.6 appear: ", e.message)
} finally {
// get the latest value in the content before timeout error, must be 0.92
expect(result.container.getElementsByTagName("span")[0].innerHTML).toEqual("0.92");
// get the number of time getValue has been called before timeout error, must be 6
expect(getValue).toBeCalledTimes(6);
}
});
参考waitFor
:
https://testing-library.com/docs/guide-disappearance/#waiting-for-appearance