英文:
Reactjs - How to unit test login form in Vitest?
问题
我正在使用React App
,并使用Vite
和Vitest
进行单元测试。
我需要测试以下情况在登录表单上:
- 输入的必填验证。
//工作正常
- 无效的用户名和密码(来自API的响应)。
//工作正常
- 登录成功。
//不符合预期
在登录成功时,它会导航到个人资料页
。在单元测试案例中,似乎导航不正常。以下是我的尝试。
> React:v18
,Vite:4.4.4
,Vitest:0.33
使用dummyjson API进行测试
> 测试文件:__tests__/Login/Validation.test.jsx
import { describe, test } from 'vitest';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import LoginPage from '../../src/pages/proteced-pages/LoginPage';
import { Provider } from 'react-redux';
import Store from '../../src/redux/store.js';
import { BrowserRouter} from 'react-router-dom';
const renderLoginPage = () => {
render(
<Provider store={Store()}>
<BrowserRouter>
<LoginPage />
</BrowserRouter>
</Provider>
);
};
describe('测试 ==> 登录表单:', () => {
// 测试 1
test('客户端验证(必填):', async () => {
renderLoginPage();
const submitButton = screen.getByRole('button', { name: /login/i });
fireEvent.click(submitButton);
await waitFor(() => {
const emailError = screen.getByText('用户名必填。', { selector: 'span' });
const passError = screen.getByText('密码必填。', { selector: 'span' });
expect(emailError).toBeInTheDocument();
expect(passError).toBeInTheDocument();
});
});
// 测试 2
test('无效的用户名,密码:', async () => {
renderLoginPage();
const emailInput = screen.getByLabelText('用户名:');
const passwordInput = screen.getByLabelText('密码:');
const submitButton = screen.getByRole('button', { name: /login/i });
fireEvent.change(emailInput, { target: { value: 'Sam12_#' } });
fireEvent.change(passwordInput, { target: { value: 'pass' } });
fireEvent.click(submitButton);
await waitFor(() => {
const invalidMessage = screen.getByText('无效的凭据', { selector: 'strong' });
expect(invalidMessage).toBeInTheDocument();
});
});
// 测试 3 不起作用
test('有效的用户名,密码:', async () => {
renderLoginPage();
screen.debug();
const emailInput = screen.getByLabelText('用户名:');
const passwordInput = screen.getByLabelText('密码:');
const submitButton = screen.getByRole('button', { name: /login/i });
fireEvent.change(emailInput, { target: { value: 'kminchelle' } });
fireEvent.change(passwordInput, { target: { value: '0lelplR' } });
fireEvent.click(submitButton);
expect(emailInput.value).toBe('kminchelle');
expect(passwordInput.value).toBe('0lelplR');
await waitFor(() => {
const profilePageHeading = screen.getByText('个人资料页', { selector: 'p' });
console.log(profilePageHeading)
expect(profilePageHeading).toBeInTheDocument();
});
});
});
错误消息
> 无法找到文本为“个人资料页”的元素,匹配选择器'p'。这可能是因为文本被多个元素分隔。在这种情况下,您可以为文本匹配器提供一个函数,以使匹配更灵活。
英文:
I am working on React App
with Vite
and Vitest
for unit testing.
I Need to test the following cases on the Login Form
- Required validation on input. `// working fine
- Invalid username, and password (response coming form API). `// working fine
- Login Success. `// Not working as expected
On login success, it is navigating to Profile Page
. When it comes to Unit test case it seems like it is not navigating properly. Here is what i Have tried so.
> React: v18
, Vite: 4.4.4
, Vitest: 0.33
Using dummyjson api for testing purpose
> Test file : __tests__/Login/Validation.test.jsx
import { describe, test } from 'vitest';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import LoginPage from '../../src/pages/proteced-pages/LoginPage';
import { Provider } from 'react-redux';
import Store from '../../src/redux/store.js';
import { BrowserRouter} from 'react-router-dom';
const renderLoginPage = () => {
render(
<Provider store={Store()}>
<BrowserRouter>
<LoginPage />
</BrowserRouter>
</Provider>
);
};
describe('Test ==> Login Form:', () => {
// TEST 1
test('Client side validation (REQUIRED): ', async () => {
renderLoginPage();
const submitButton = screen.getByRole('button', { name: /login/i });
fireEvent.click(submitButton);
await waitFor(() => {
const emailError = screen.getByText('username is required.', { selector: 'span' });
const passError = screen.getByText('Password is required.', { selector: 'span' });
expect(emailError).toBeInTheDocument();
expect(passError).toBeInTheDocument();
});
});
// TEST 2
test('Invalid username, password: ', async () => {
renderLoginPage();
const emailInput = screen.getByLabelText('username:');
const passwordInput = screen.getByLabelText('Password:');
const submitButton = screen.getByRole('button', { name: /login/i });
fireEvent.change(emailInput, { target: { value: 'Sam12_#' } });
fireEvent.change(passwordInput, { target: { value: 'pass' } });
fireEvent.click(submitButton);
await waitFor(() => {
const invalidMessage = screen.getByText('Invalid credentials', { selector: 'strong' });
expect(invalidMessage).toBeInTheDocument();
});
});
// TEST 3 Not working
test('Valid username, password: ', async () => {
renderLoginPage();
screen.debug();
const emailInput = screen.getByLabelText('username:');
const passwordInput = screen.getByLabelText('Password:');
const submitButton = screen.getByRole('button', { name: /login/i });
fireEvent.change(emailInput, { target: { value: 'kminchelle' } });
fireEvent.change(passwordInput, { target: { value: '0lelplR' } });
fireEvent.click(submitButton);
expect(emailInput.value).toBe('kminchelle');
expect(passwordInput.value).toBe('0lelplR');
await waitFor(() => {
const profilePageHeading = screen.getByText('Profile pages', { selector: 'p' });
console.log(profilePageHeading)
expect(profilePageHeading).toBeInTheDocument();
});
});
});
Error message
> Unable to find an element with the text: Profile pages, which matches selector 'p'. 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.
答案1
得分: 0
getByText
是同步的,即它不会等待元素出现。请使用 findByText
(await findByText(//...
) 替代。
英文:
getByText
is synchronous, i.e. it doesn't "wait" for the element to appear. Use findByText
(await findByText(//...
) instead.
答案2
得分: 0
我已经用MemoryRouter
替换了BrowserRouter
来解决问题。
>如果有更好的建议,欢迎分享。
以下是对我有效的解决方案
// 导入个人资料页面
import ProfilePage from '../../src/pages/auth-pages/ProfilePage';
// 用MemoryRouter替换BrowserRouter
const renderLoginPage = () => {
render(
<Provider store={Store()}>
<MemoryRouter initialEntries={['/login']}>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/profile" element={<ProfilePage />} />
</Routes>
</MemoryRouter>
</Provider>
);
};
describe('Test ==> 登录表单:', () => {
// 测试 3
test('有效的用户名和密码: ', async () => {
renderLoginPage();
const emailInput = screen.getByLabelText('用户名:');
const passwordInput = screen.getByLabelText('密码:');
const submitButton = screen.getByRole('button', { name: /登录/i });
fireEvent.change(emailInput, { target: { value: 'kminchelle' } });
fireEvent.change(passwordInput, { target: { value: '0lelplR' } });
fireEvent.click(submitButton);
expect(emailInput.value).toBe('kminchelle');
expect(passwordInput.value).toBe('0lelplR');
await waitFor(() => {
const profilePageHeading = screen.getByText('个人资料页面', { selector: 'p' });
expect(profilePageHeading).toBeInTheDocument();
});
});
});
英文:
I have fixed the problem replacing BrowserRouter
with MemoryRouter
>If someone has better suggestion then he is welcome to share.
Here is the solution that worked for me
//import profile page
import ProfilePage from '../../src/pages/auth-pages/ProfilePage';
//replacing BrowserRouter with MemoryRouter
const renderLoginPage = () => {
render(
<Provider store={Store()}>
<MemoryRouter initialEntries={['/login']}>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/profile" element={<ProfilePage />} />
</Routes>
</MemoryRouter>
</Provider>
);
};
describe('Test ==> Login Form:', () => {
// TEST 3
test('Valid username, password: ', async () => {
renderLoginPage();
const emailInput = screen.getByLabelText('username:');
const passwordInput = screen.getByLabelText('Password:');
const submitButton = screen.getByRole('button', { name: /login/i });
fireEvent.change(emailInput, { target: { value: 'kminchelle' } });
fireEvent.change(passwordInput, { target: { value: '0lelplR' } });
fireEvent.click(submitButton);
expect(emailInput.value).toBe('kminchelle');
expect(passwordInput.value).toBe('0lelplR');
await waitFor(() => {
const profilePageHeading = screen.getByText('Profile pages', { selector: 'p' });
expect(profilePageHeading).toBeInTheDocument();
});
});
});
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论