英文:
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();
});
});
});
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论