Integration testing checks if different parts of your Remix app work well together. Testing Library helps you test your app like a user would.
Integration testing with Testing Library in Remix
import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { MemoryRouter } from 'react-router-dom'; import MyComponent from '~/components/MyComponent'; // Render component inside router for Remix routes render( <MemoryRouter> <MyComponent /> </MemoryRouter> ); // Find elements const button = screen.getByRole('button', { name: /submit/i }); // Simulate user click await userEvent.click(button); // Assert expected output expect(screen.getByText(/success/i)).toBeInTheDocument();
Use MemoryRouter to simulate routing in tests.
Use screen to find elements by role, text, or label for accessibility.
render(<MyComponent />);
/home to test route-specific behavior.render(
<MemoryRouter initialEntries={["/home"]}>
<MyComponent />
</MemoryRouter>
);const input = screen.getByLabelText('Username'); await userEvent.type(input, 'myname');
This test renders a simple component with a button. When the button is clicked, a greeting message appears. The test simulates the click and checks if the message shows up.
import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { MemoryRouter } from 'react-router-dom'; import { useState } from 'react'; // Simple component with button and message function Greeting() { const [greeted, setGreeted] = useState(false); return ( <div> <button onClick={() => setGreeted(true)}>Say Hello</button> {greeted && <p>Hello, user!</p>} </div> ); } // Test async function testGreeting() { render( <MemoryRouter> <Greeting /> </MemoryRouter> ); const button = screen.getByRole('button', { name: /say hello/i }); await userEvent.click(button); const message = screen.getByText(/hello, user!/i); console.log(message.textContent); } testGreeting();
Always test components inside a router if they use routing features.
Use roles and labels to find elements for better accessibility and reliable tests.
Use await with userEvent to handle async user actions properly.
Integration testing checks how parts of your Remix app work together.
Testing Library helps simulate user actions and find elements accessibly.
Render components inside MemoryRouter to test routing behavior.
Practice
Solution
Step 1: Understand integration testing goal
Integration testing focuses on how multiple parts of an app interact, not isolated units.Step 2: Match Testing Library use
Testing Library simulates user actions and checks UI behavior, which fits integration testing.Final Answer:
To check how different parts of the app work together -> Option AQuick Check:
Integration testing = check parts working together [OK]
- Confusing integration with unit testing
- Thinking integration tests check performance
- Assuming integration tests check deployment
Solution
Step 1: Identify routing context for tests
MemoryRouter is used in tests to simulate routing without a real browser environment.Step 2: Check other options
BrowserRouter requires a real browser, RouterProvider is Remix-specific but not typical for Testing Library integration tests.Final Answer:
render(<MemoryRouter><MyComponent /></MemoryRouter>) -> Option CQuick Check:
Use MemoryRouter for routing in tests [OK]
- Using BrowserRouter which needs a real browser
- Not wrapping component in any router
- Confusing RouterProvider with test router
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import UserProfile from '~/components/UserProfile';
test('shows user name after loading', () => {
render();
const nameElement = screen.getByText(/loading/i);
expect(nameElement).toBeInTheDocument();
});Solution
Step 1: Analyze render and query
The component is rendered inside MemoryRouter, so routing context is correct. The test looks for text matching 'loading' ignoring case.Step 2: Understand UserProfile behavior
Typically, UserProfile shows 'loading' text initially before user data loads, so the text should be present immediately.Final Answer:
Test passes because 'loading' text is found -> Option AQuick Check:
Loading text present = test passes [OK]
- Assuming user name appears immediately
- Forgetting to wrap in MemoryRouter
- Confusing syntax errors with runtime failures
render();
const button = screen.getByText('Submit');
button.click();What is the likely cause and fix?
Solution
Step 1: Check error cause
Error says no element with text 'Submit' found, so likely the button text is different or missing.Step 2: Fix by verifying button text
Confirm the actual button text in MyForm matches 'Submit' exactly or adjust the query to match the real text.Final Answer:
The button text is different; fix by using correct text -> Option DQuick Check:
Text mismatch causes getByText failure [OK]
- Assuming click() works without userEvent
- Wrapping component unnecessarily in router
- Using getByText as async without reason
Solution
Step 1: Include routing context
MemoryRouter simulates routing in tests without needing a real browser, so wrap the component in it.Step 2: Mock data fetching
Mocking fetch with a test server or mock service avoids real network calls and controls test data.Step 3: Avoid real API calls and BrowserRouter
Real API calls slow tests and BrowserRouter requires a browser environment, not suitable for integration tests.Final Answer:
Render the component inside MemoryRouter and mock the fetch with a test server -> Option BQuick Check:
MemoryRouter + fetch mock = full integration test [OK]
- Using BrowserRouter in tests
- Not mocking network requests
- Skipping router context in route tests
Ready to prove it?
Pass the test and this pattern moves unknown
