React 测试库看不到 useState 更新函数的结果
React Testing Library can't see result of useState update function
问题:
我有一个 React 组件,它有一个简单的表单和一个用于在提交时显示输出的区域。
我已经手动测试并确认提交表单会按预期输出值。但是 我无法让 React 测试库做出相同的断言。
据我所知,似乎没有看到更新的页面内容。
这是测试失败的输出:
● User should be able to submit their first name
expect(element).toHaveTextContent()
Expected element to have text content:
firstName: Matthew
Received:
firstName:
47 |
48 | expect(output.firstName).toHaveTextContent("firstName:");
> 49 | expect(output.firstName).toHaveTextContent("firstName: Matthew");
| ^
50 | });
51 |
52 | /*
组件如下:
import React, { useState } from "react";
import './Playground.css';
const Playground = ({
firstName,
lastName,
email,
subject,
message
}) => {
let [myState, setMyState] = useState({
firstName: firstName ? firstName : "",
lastName: lastName ? lastName : "",
email: email ? email : "",
subject: subject ? subject : "",
message: message ? message : ""
});
function submit(event) {
event.preventDefault();
const form = event.target;
setMyState({
...myState,
firstName: form?.firstName?.value ? form?.firstName?.value : myState.firstName,
lastName: form?.lastName?.value ? form.lastName?.value : myState.lastName,
email: form?.email?.value ? form?.email?.value : myState.email,
subject: form?.subject?.value ? form?.subject?.value : myState.subject,
message: form?.message?.value ? form?.message?.value : myState.message
});
}
return (
<section data-testid="playground">
<h1>Form</h1>
<section data-testid="input">
<form onSubmit={submit}>
<label htmlFor="firstName">First Name</label>
<input type="text" id="firstName" placeholder="Jane" />
<label htmlFor="lastName">Last Name</label>
<input type="text" id="lastName" placeholder="Doe" />
<label htmlFor="email">Email</label>
<input type="text" id="email" placeholder="jane.doe@example.com" />
<label htmlFor="subject">Subject</label>
<input type="text" id="subject" placeholder="Subject" />
<label htmlFor="message">Message</label>
<textarea rows="3" id="message" type="text" placeholder="Message"></textarea>
<button type="submit" name="update">Update</button>
</form>
</section>
<section data-testid="output">
<div>firstName: {myState.firstName}</div>
<div>lastName: {myState.lastName}</div>
<div>email: {myState.email}</div>
<div>subject: {myState.subject}</div>
<div>message: {myState.message}</div>
</section>
</section>
);
};
export default Playground;
这是有问题的测试(第 49 行的断言失败):
import React from 'react';
import {render, screen} from "@testing-library/react";
import userEvent from '@testing-library/user-event';
import Playground from "./Playground";
function getFormFields (screen) {
const formFields = {};
formFields.firstName = screen.getByLabelText(/first name/i);
formFields.lastName = screen.getByLabelText(/last name/i);
formFields.email = screen.getByLabelText(/email/i);
formFields.subject = screen.getByLabelText(/subject/i);
formFields.message = screen.getByLabelText(/^message$/i);
return formFields;
}
function getOutput (screen) {
const output = {};
output.firstName = screen.getByText(/^firstname:/i);
output.lastName = screen.getByText(/^lastname:/i);
output.email = screen.getByText(/^email:/i);
output.subject = screen.getByText(/^subject:/i);
output.message = screen.getByText(/^message:/i);
return output;
}
test('Should have an editable First Name field', () => {
render(<Playground />)
const fields = getFormFields(screen);
userEvent.type(fields.firstName, "Matthew");
expect(fields.firstName).toHaveValue("Matthew");
});
test('User should be able to submit their first name', async () => {
render(<Playground />)
const fields = getFormFields(screen),
submit = screen.getByRole('button', { name: /update/i }),
output = getOutput(screen);
userEvent.type(fields.firstName, "Matthew");
userEvent.click(submit);
expect(output.firstName).toHaveTextContent("firstName:");
expect(output.firstName).toHaveTextContent("Matthew");
});
我尝试过的事情
- 对文本内容使用 findBy 而不是 getBy
- 在执行更新点击后声明断言的目标元素
- 使用等待
- 我现在忘记了很多其他东西
最后的想法
这让我抓狂,因为它 看起来 就像一个非常简单的问题(我并不是在做火箭手术)。如果能得到任何帮助,我将不胜感激!
谢谢!
您需要等待 UI 在某个事件(例如点击)后更改:
import { render, screen, waitFor } from '@testing-library/react';
...
userEvent.click(submit);
await waitFor(() => {
// The first assertion may not be necessary, as it never changes?
// And it's not recommended to have multiple assertions in waitFor.
expect(output.firstName).toHaveTextContent("firstName:");
expect(output.firstName).toHaveTextContent("Matthew");
});
编辑
还有一个问题,您需要更改您的提交处理程序:
function submit(event) {
event.preventDefault();
const form = event.target.elements; // <-- note that it uses 'elements'
...
有趣的是,它在“现实生活”中有效,但在测试环境中无效。但是对于 elements
它在这两种情况下都有效。
您可以查看此 MDN article 了解更多信息。
问题:
我有一个 React 组件,它有一个简单的表单和一个用于在提交时显示输出的区域。
我已经手动测试并确认提交表单会按预期输出值。但是 我无法让 React 测试库做出相同的断言。
据我所知,似乎没有看到更新的页面内容。
这是测试失败的输出:
● User should be able to submit their first name
expect(element).toHaveTextContent()
Expected element to have text content:
firstName: Matthew
Received:
firstName:
47 |
48 | expect(output.firstName).toHaveTextContent("firstName:");
> 49 | expect(output.firstName).toHaveTextContent("firstName: Matthew");
| ^
50 | });
51 |
52 | /*
组件如下:
import React, { useState } from "react";
import './Playground.css';
const Playground = ({
firstName,
lastName,
email,
subject,
message
}) => {
let [myState, setMyState] = useState({
firstName: firstName ? firstName : "",
lastName: lastName ? lastName : "",
email: email ? email : "",
subject: subject ? subject : "",
message: message ? message : ""
});
function submit(event) {
event.preventDefault();
const form = event.target;
setMyState({
...myState,
firstName: form?.firstName?.value ? form?.firstName?.value : myState.firstName,
lastName: form?.lastName?.value ? form.lastName?.value : myState.lastName,
email: form?.email?.value ? form?.email?.value : myState.email,
subject: form?.subject?.value ? form?.subject?.value : myState.subject,
message: form?.message?.value ? form?.message?.value : myState.message
});
}
return (
<section data-testid="playground">
<h1>Form</h1>
<section data-testid="input">
<form onSubmit={submit}>
<label htmlFor="firstName">First Name</label>
<input type="text" id="firstName" placeholder="Jane" />
<label htmlFor="lastName">Last Name</label>
<input type="text" id="lastName" placeholder="Doe" />
<label htmlFor="email">Email</label>
<input type="text" id="email" placeholder="jane.doe@example.com" />
<label htmlFor="subject">Subject</label>
<input type="text" id="subject" placeholder="Subject" />
<label htmlFor="message">Message</label>
<textarea rows="3" id="message" type="text" placeholder="Message"></textarea>
<button type="submit" name="update">Update</button>
</form>
</section>
<section data-testid="output">
<div>firstName: {myState.firstName}</div>
<div>lastName: {myState.lastName}</div>
<div>email: {myState.email}</div>
<div>subject: {myState.subject}</div>
<div>message: {myState.message}</div>
</section>
</section>
);
};
export default Playground;
这是有问题的测试(第 49 行的断言失败):
import React from 'react';
import {render, screen} from "@testing-library/react";
import userEvent from '@testing-library/user-event';
import Playground from "./Playground";
function getFormFields (screen) {
const formFields = {};
formFields.firstName = screen.getByLabelText(/first name/i);
formFields.lastName = screen.getByLabelText(/last name/i);
formFields.email = screen.getByLabelText(/email/i);
formFields.subject = screen.getByLabelText(/subject/i);
formFields.message = screen.getByLabelText(/^message$/i);
return formFields;
}
function getOutput (screen) {
const output = {};
output.firstName = screen.getByText(/^firstname:/i);
output.lastName = screen.getByText(/^lastname:/i);
output.email = screen.getByText(/^email:/i);
output.subject = screen.getByText(/^subject:/i);
output.message = screen.getByText(/^message:/i);
return output;
}
test('Should have an editable First Name field', () => {
render(<Playground />)
const fields = getFormFields(screen);
userEvent.type(fields.firstName, "Matthew");
expect(fields.firstName).toHaveValue("Matthew");
});
test('User should be able to submit their first name', async () => {
render(<Playground />)
const fields = getFormFields(screen),
submit = screen.getByRole('button', { name: /update/i }),
output = getOutput(screen);
userEvent.type(fields.firstName, "Matthew");
userEvent.click(submit);
expect(output.firstName).toHaveTextContent("firstName:");
expect(output.firstName).toHaveTextContent("Matthew");
});
我尝试过的事情
- 对文本内容使用 findBy 而不是 getBy
- 在执行更新点击后声明断言的目标元素
- 使用等待
- 我现在忘记了很多其他东西
最后的想法
这让我抓狂,因为它 看起来 就像一个非常简单的问题(我并不是在做火箭手术)。如果能得到任何帮助,我将不胜感激!
谢谢!
您需要等待 UI 在某个事件(例如点击)后更改:
import { render, screen, waitFor } from '@testing-library/react';
...
userEvent.click(submit);
await waitFor(() => {
// The first assertion may not be necessary, as it never changes?
// And it's not recommended to have multiple assertions in waitFor.
expect(output.firstName).toHaveTextContent("firstName:");
expect(output.firstName).toHaveTextContent("Matthew");
});
编辑
还有一个问题,您需要更改您的提交处理程序:
function submit(event) {
event.preventDefault();
const form = event.target.elements; // <-- note that it uses 'elements'
...
有趣的是,它在“现实生活”中有效,但在测试环境中无效。但是对于 elements
它在这两种情况下都有效。
您可以查看此 MDN article 了解更多信息。