使用react-testing-library测试Material-UI闪信组件失败排查
Hey there! Let's work through getting your login error Snackbar test working right. It sounds like you've already nailed triggering the failed login action, but the Snackbar's content is eluding your test assertions—here are the most likely fixes to try:
1. Make sure your test includes the global Snackbar context
Since your Snackbar lives in App.js, if you're only rendering LoginContainer in your test, the Snackbar isn't part of the test DOM tree at all! You need to wrap your component in the full App or at least the context/provider that manages the Snackbar. For example:
import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import App from '../App'; import { MemoryRouter } from 'react-router-dom'; test('shows error Snackbar on failed login', async () => { // Render the full app with login route preloaded render( <MemoryRouter initialEntries={['/login']}> <App /> </MemoryRouter> ); // Fill in invalid credentials and submit const usernameInput = screen.getByLabelText('아이디'); const passwordInput = screen.getByLabelText('비밀번호'); const submitBtn = screen.getByRole('button', { name: '로그인' }); fireEvent.change(usernameInput, { target: { value: 'bad-user' } }); fireEvent.change(passwordInput, { target: { value: 'bad-pass' } }); fireEvent.click(submitBtn); // Wait for the Snackbar to render (it's async with animation!) await waitFor(() => { const errorMsg = screen.getByText('아이디 또는 비밀번호 오류입니다.'); expect(errorMsg).toBeInTheDocument(); expect(errorMsg).toBeVisible(); }); });
2. Account for Snackbar's async rendering & animations
Material-UI's Snackbar uses transition animations by default, so your test might be checking for the text before the component finishes rendering. waitFor is perfect here—it retries your assertion until it passes or times out (default 1000ms, which is enough for most Snackbar transitions).
If your Snackbar has a very short autoHideDuration, you can extend the timeout in waitFor to avoid missing it:
await waitFor(() => { expect(screen.getByText('아이디 또는 비밀번호 오류입니다.')).toBeVisible(); }, { timeout: 3000 }); // Give it 3 seconds instead of 1
3. Double-check your test IDs and selectors
If you're using getByTestId, make sure the actual Snackbar content element has the correct data-testid attribute. For example, if you're using Material-UI's Alert inside the Snackbar:
// In your global Snackbar component (App.js) <Snackbar open={isSnackbarOpen} autoHideDuration={6000} onClose={closeSnackbar}> <Alert severity="error" data-testid="client-msg"> {snackbarMessage} </Alert> </Snackbar>
Then your test can target it directly:
await waitFor(() => { const snackbar = screen.getByTestId('client-msg'); expect(snackbar).toHaveTextContent('아이디 또는 비밀번호 오류입니다.'); });
4. Verify your error trigger logic is working
Add a quick check to confirm your failed login is actually dispatching the action that shows the Snackbar. You can mock your login action to ensure it's called and rejects properly:
import { loginAction } from '../actions/authActions'; jest.mock('../actions/authActions'); test('displays error on failed login', async () => { // Mock the login action to reject with your error message loginAction.mockRejectedValue(new Error('아이디 또는 비밀번호 오류입니다.')); // ... render component and submit invalid credentials ... await waitFor(() => { expect(loginAction).toHaveBeenCalledWith('bad-user', 'bad-pass'); expect(screen.getByText('아이디 또는 비밀번호 오류입니다.')).toBeVisible(); }); });
Most of the time, the issue boils down to either not including the global Snackbar in your test tree or not waiting for the async animation to finish. Give these steps a shot, and your test should start passing!
内容的提问来源于stack exchange,提问作者Jiaah

