使用 material-ui 的 react-testing-library 时出现问题
issue working with react-testing-library with material-ui
尝试用 react-testing-library 编写我的第一个测试,但它似乎无法获取正确的 material-ui 元素。
https://codesandbox.io/s/lx5nl1839z
我收到一条错误消息,指出事件从未触发。
Expected mock function to have been called one time, but it was called zero times.
当我将 button
从 material ui 更改为常规按钮而不是 Button 时,它就可以工作了。
这是我的测试
test('calls with user email and password', () => {
const login = jest.fn();
const error = null as any;
const { getByLabelText, getByText } = render(
<LoginForm login={login} error={error} />
);
const email = getByLabelText('Email');
const password = getByLabelText('Password');
(email as any).value = 'leoq91@gmail.com';
(password as any).value = 'password';
fireEvent.click(getByText('Login'));
expect(login).toHaveBeenCalledTimes(1);
expect(login).toHaveBeenCalledWith({
email: 'leoq91@gmail.com',
password: 'password',
});
});
这是我的组件:
export const LoginForm: FunctionComponent<IProps> = ({ login, error }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<Paper style={{ width: '400px', padding: '30px', margin: '0 auto' }}>
<form
onSubmit={e => {
e.preventDefault();
return login({ email, password });
}}
>
<TextField
id="email"
label="Email"
onChange={e => setEmail(e.target.value)}
fullWidth
variant="outlined"
error={Boolean(getEmailError(email))}
helperText={getEmailError(email)}
/>
<TextField
id="password"
label="Password"
type="password"
style={{ marginTop: '10px' }}
onChange={e => setPassword(e.target.value)}
fullWidth
variant="outlined"
/>
<LoginError error={error} />
<Button
variant="contained"
color="primary"
type="submit"
fullWidth
// disabled={isDisabled(email, password)}
style={{
margin: '20px 0px',
}}
>
Login
</Button>
</form>
<Divider />
<Typography style={{ margin: '10px auto' }}>Forgot password?</Typography>
</Paper>
);
};
我看到很多人对 material-ui 有疑问。我的猜测是 Button
试图做一些花哨的事情并打破正常的 HTML 事件流。
我的建议是使用 jest.mock() 和 mock Button
来渲染 button
。
您的文本字段不受控制。您应该传递实际值并在更改事件中设置实际值。
您当前在测试中触发了两次提交。
运行 使用这些修复的测试具有正确的控制台输出,但出于某种原因,codesandbox 忽略了笑话匹配器。我不知道那里发生了什么,但通过上述修复,它的要点起作用了。
首先,codesandbox不靠谱……
codesandbox is somehow running the tests in the browser and not in jsdom.
这是问题所在:
https://github.com/kentcdodds/react-testing-library/issues/234
要点是因为它单击的是 submit
范围,而不是按钮。它应该冒泡了,但它没有。
这是一个使用 Typescript 的本地服务器的工作示例。
test('calls with user email and password', () => {
const login = jest.fn();
const { getByLabelText, container } = render(
<LoginForm login={login} error={null} />
);
const emailNode = getByLabelText('Email');
const passwordNode = getByLabelText('Password');
if (
emailNode instanceof HTMLInputElement &&
passwordNode instanceof HTMLInputElement
) {
fireEvent.change(emailNode, { target: { value: 'leoq91@gmail.com' } });
fireEvent.change(passwordNode, { target: { value: 'password' } });
const form = container.querySelector('form') as HTMLFormElement;
form.dispatchEvent(new Event('submit'));
expect(login).toHaveBeenCalledTimes(1);
expect(login).toHaveBeenCalledWith({
email: emailNode.value,
password: passwordNode.value,
});
} else {
expect(false);
}
});
我已经为此纠结了一段时间,终于找到了一些东西可以将 data-testid 放在输入上,而不是 material-[= 中组成 TextField 的 div 周围21=].
您应该像这样在 inputProps 中设置 data-testid
:
<TextField
type="password"
variant="outlined"
required
value={password}
onChange={onChangePassword}
inputProps={{
'data-testid': 'testPassword'
}}
/>
然后您可以像那样在测试中访问它并模拟新的用户输入:
const passwordInput = getByTestId('testPassword');
fireEvent.change(passwordInput, { target: { value: VALID_PASSWORD } });
尝试用 react-testing-library 编写我的第一个测试,但它似乎无法获取正确的 material-ui 元素。
https://codesandbox.io/s/lx5nl1839z
我收到一条错误消息,指出事件从未触发。
Expected mock function to have been called one time, but it was called zero times.
当我将 button
从 material ui 更改为常规按钮而不是 Button 时,它就可以工作了。
这是我的测试
test('calls with user email and password', () => {
const login = jest.fn();
const error = null as any;
const { getByLabelText, getByText } = render(
<LoginForm login={login} error={error} />
);
const email = getByLabelText('Email');
const password = getByLabelText('Password');
(email as any).value = 'leoq91@gmail.com';
(password as any).value = 'password';
fireEvent.click(getByText('Login'));
expect(login).toHaveBeenCalledTimes(1);
expect(login).toHaveBeenCalledWith({
email: 'leoq91@gmail.com',
password: 'password',
});
});
这是我的组件:
export const LoginForm: FunctionComponent<IProps> = ({ login, error }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<Paper style={{ width: '400px', padding: '30px', margin: '0 auto' }}>
<form
onSubmit={e => {
e.preventDefault();
return login({ email, password });
}}
>
<TextField
id="email"
label="Email"
onChange={e => setEmail(e.target.value)}
fullWidth
variant="outlined"
error={Boolean(getEmailError(email))}
helperText={getEmailError(email)}
/>
<TextField
id="password"
label="Password"
type="password"
style={{ marginTop: '10px' }}
onChange={e => setPassword(e.target.value)}
fullWidth
variant="outlined"
/>
<LoginError error={error} />
<Button
variant="contained"
color="primary"
type="submit"
fullWidth
// disabled={isDisabled(email, password)}
style={{
margin: '20px 0px',
}}
>
Login
</Button>
</form>
<Divider />
<Typography style={{ margin: '10px auto' }}>Forgot password?</Typography>
</Paper>
);
};
我看到很多人对 material-ui 有疑问。我的猜测是 Button
试图做一些花哨的事情并打破正常的 HTML 事件流。
我的建议是使用 jest.mock() 和 mock Button
来渲染 button
。
您的文本字段不受控制。您应该传递实际值并在更改事件中设置实际值。 您当前在测试中触发了两次提交。
运行 使用这些修复的测试具有正确的控制台输出,但出于某种原因,codesandbox 忽略了笑话匹配器。我不知道那里发生了什么,但通过上述修复,它的要点起作用了。
首先,codesandbox不靠谱……
codesandbox is somehow running the tests in the browser and not in jsdom.
这是问题所在: https://github.com/kentcdodds/react-testing-library/issues/234
要点是因为它单击的是 submit
范围,而不是按钮。它应该冒泡了,但它没有。
这是一个使用 Typescript 的本地服务器的工作示例。
test('calls with user email and password', () => {
const login = jest.fn();
const { getByLabelText, container } = render(
<LoginForm login={login} error={null} />
);
const emailNode = getByLabelText('Email');
const passwordNode = getByLabelText('Password');
if (
emailNode instanceof HTMLInputElement &&
passwordNode instanceof HTMLInputElement
) {
fireEvent.change(emailNode, { target: { value: 'leoq91@gmail.com' } });
fireEvent.change(passwordNode, { target: { value: 'password' } });
const form = container.querySelector('form') as HTMLFormElement;
form.dispatchEvent(new Event('submit'));
expect(login).toHaveBeenCalledTimes(1);
expect(login).toHaveBeenCalledWith({
email: emailNode.value,
password: passwordNode.value,
});
} else {
expect(false);
}
});
我已经为此纠结了一段时间,终于找到了一些东西可以将 data-testid 放在输入上,而不是 material-[= 中组成 TextField 的 div 周围21=].
您应该像这样在 inputProps 中设置 data-testid
:
<TextField
type="password"
variant="outlined"
required
value={password}
onChange={onChangePassword}
inputProps={{
'data-testid': 'testPassword'
}}
/>
然后您可以像那样在测试中访问它并模拟新的用户输入:
const passwordInput = getByTestId('testPassword');
fireEvent.change(passwordInput, { target: { value: VALID_PASSWORD } });