How to Replace React Testing Library with Vitest Browser Mode

by Ada
Last updated on May 24, 2024

How to Replace React Testing Library with Vitest Browser Mode

Vitest Browser Mode allows you to run tests in real browsers instead of jsdom. This guide shows you how to migrate from React Testing Library to Vitest Browser Mode.

Installation

First, install the required packages:

bash
npm install -D vitest @vitest/browser @vitest/browser-playwright vitest-browser-react playwright

Configuration

Update your vitest.config.ts:

typescript
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import { playwright } from '@vitest/browser-playwright'

export default defineConfig({
  plugins: [react()],
  test: {
    browser: {
      provider: playwright(),
      enabled: true,
      instances: [{ browser: 'chromium' }],
    },
  },
})

Migration Guide

Before: React Testing Library

tsx
import { render, screen, fireEvent } from '@testing-library/react'
import { expect, test } from 'vitest'
import Counter from './Counter'

test('increments counter', () => {
  render(<Counter />)

  const button = screen.getByRole('button', { name: /increment/i })
  const count = screen.getByText(/count: 0/i)

  expect(count).toBeInTheDocument()

  fireEvent.click(button)

  expect(screen.getByText(/count: 1/i)).toBeInTheDocument()
})

After: Vitest Browser Mode

tsx
import { render } from 'vitest-browser-react'
import { page, userEvent } from 'vitest/browser'
import { expect, test } from 'vitest'
import Counter from './Counter'

test('increments counter', async () => {
  render(<Counter />)

  const button = page.getByRole('button', { name: /increment/i })
  const count = page.getByText(/count: 0/i)

  await expect.element(count).toBeInTheDocument()

  await userEvent.click(button)

  await expect.element(page.getByText(/count: 1/i)).toBeInTheDocument()
})

Key Differences

1. Imports

RTL:

tsx
import { render, screen } from '@testing-library/react'

Browser Mode:

tsx
import { render } from 'vitest-browser-react'
import { page } from 'vitest/browser'

2. Querying Elements

RTL:

tsx
screen.getByRole('button')
screen.getByText(/hello/i)

Browser Mode:

tsx
page.getByRole('button')
page.getByText(/hello/i)

3. Assertions

RTL:

tsx
expect(button).toBeInTheDocument()

Browser Mode:

tsx
await expect.element(button).toBeInTheDocument()

4. User Interactions

RTL:

tsx
import userEvent from '@testing-library/user-event'

await userEvent.click(button)
await userEvent.type(input, 'hello')

Browser Mode:

tsx
import { userEvent } from 'vitest/browser'

await userEvent.click(button)
await userEvent.fill(input, 'hello')

Form Example

tsx
import { render } from 'vitest-browser-react'
import { page, userEvent } from 'vitest/browser'
import { expect, test } from 'vitest'

test('submits login form', async () => {
  render(<LoginForm />)

  const emailInput = page.getByLabelText(/email/i)
  const passwordInput = page.getByLabelText(/password/i)
  const submitButton = page.getByRole('button', { name: /sign in/i })

  await userEvent.fill(emailInput, '[email protected]')
  await userEvent.fill(passwordInput, 'password123')
  await userEvent.click(submitButton)

  await expect.element(page.getByText(/welcome back/i)).toBeInTheDocument()
})

Async Data Example

tsx
test('displays user profile', async () => {
  render(<UserProfile userId="123" />)

  // Wait for loading state to disappear
  await expect.element(page.getByText(/loading/i)).not.toBeInTheDocument()

  // Check data is displayed
  await expect.element(page.getByText(/john doe/i)).toBeInTheDocument()
})

Running Tests

bash
# Run all tests
npm run vitest

# Run specific file
npm run vitest src/components/Button.test.tsx

# Run with UI
npm run vitest -- --ui

Browser Options

You can test in different browsers:

typescript
export default defineConfig({
  test: {
    browser: {
      provider: playwright(),
      enabled: true,
      instances: [
        { browser: 'chromium' },
        { browser: 'firefox' },
        { browser: 'webkit' },
      ],
    },
  },
})

Benefits

  • Tests run in real browsers, not jsdom
  • More accurate browser behavior
  • Better debugging with browser DevTools
  • Support for multiple browsers
  • Visual testing capabilities

Tips

  1. All assertions must use await expect.element()
  2. All user interactions must be awaited
  3. Use page instead of screen for queries
  4. Import render from vitest-browser-react, not @testing-library/react
  5. Use userEvent.fill() instead of userEvent.type() for inputs

Resources