react18-enzyme-to-rtl

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React 18 Enzyme → RTL Migration

React 18 Enzyme → RTL 迁移

Enzyme has no React 18 adapter and no React 18 support path. All Enzyme tests must be rewritten using React Testing Library.
Enzyme没有适配React 18的适配器,也没有支持React 18的后续规划。所有Enzyme测试都必须使用React Testing Library重写。

The Philosophy Shift (Read This First)

理念转变(请优先阅读)

Enzyme tests implementation. RTL tests behavior.
jsx
// Enzyme: tests that the component has the right internal state
expect(wrapper.state('count')).toBe(3);
expect(wrapper.instance().handleClick).toBeDefined();
expect(wrapper.find('Button').prop('disabled')).toBe(true);

// RTL: tests what the user actually sees and can do
expect(screen.getByText('Count: 3')).toBeInTheDocument();
expect(screen.getByRole('button', { name: /submit/i })).toBeDisabled();
This is not a 1:1 translation. Enzyme tests that verify internal state or instance methods don't have RTL equivalents - because RTL intentionally doesn't expose internals. Rewrite the test to assert the visible outcome instead.
Enzyme测试实现逻辑,RTL测试用户行为。
jsx
// Enzyme: tests that the component has the right internal state
expect(wrapper.state('count')).toBe(3);
expect(wrapper.instance().handleClick).toBeDefined();
expect(wrapper.find('Button').prop('disabled')).toBe(true);

// RTL: tests what the user actually sees and can do
expect(screen.getByText('Count: 3')).toBeInTheDocument();
expect(screen.getByRole('button', { name: /submit/i })).toBeDisabled();
这不是1:1的直译。验证内部状态或实例方法的Enzyme测试没有对应的RTL等价写法——因为RTL刻意不暴露内部逻辑。请重写测试,转而断言可见的输出结果。

API Map

API 映射

For complete before/after code for each Enzyme API, read:
  • references/enzyme-api-map.md
    - full mapping: shallow, mount, find, simulate, prop, state, instance, configure
  • references/async-patterns.md
    - waitFor, findBy, act(), Apollo MockedProvider, loading states, error states
要获取每个Enzyme API对应的完整前后对照代码,请阅读:
  • references/enzyme-api-map.md
    - 完整映射:shallow, mount, find, simulate, prop, state, instance, configure
  • references/async-patterns.md
    - waitFor, findBy, act(), Apollo MockedProvider, 加载状态, 错误状态

Core Rewrite Template

核心重写模板

jsx
// Every Enzyme test rewrites to this shape:
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import MyComponent from './MyComponent';

describe('MyComponent', () => {
  it('does the thing', async () => {
    // 1. Render (replaces shallow/mount)
    render(<MyComponent prop="value" />);

    // 2. Query (replaces wrapper.find())
    const button = screen.getByRole('button', { name: /submit/i });

    // 3. Interact (replaces simulate())
    await userEvent.setup().click(button);

    // 4. Assert on visible output (replaces wrapper.state() / wrapper.prop())
    expect(screen.getByText('Submitted!')).toBeInTheDocument();
  });
});
jsx
// Every Enzyme test rewrites to this shape:
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import MyComponent from './MyComponent';

describe('MyComponent', () => {
  it('does the thing', async () => {
    // 1. Render (replaces shallow/mount)
    render(<MyComponent prop="value" />);

    // 2. Query (replaces wrapper.find())
    const button = screen.getByRole('button', { name: /submit/i });

    // 3. Interact (replaces simulate())
    await userEvent.setup().click(button);

    // 4. Assert on visible output (replaces wrapper.state() / wrapper.prop())
    expect(screen.getByText('Submitted!')).toBeInTheDocument();
  });
});

RTL Query Priority (use in this order)

RTL 查询优先级(按此顺序使用)

  1. getByRole
    - matches accessible roles (button, textbox, heading, checkbox, etc.)
  2. getByLabelText
    - form fields linked to labels
  3. getByPlaceholderText
    - input placeholders
  4. getByText
    - visible text content
  5. getByDisplayValue
    - current value of input/select/textarea
  6. getByAltText
    - image alt text
  7. getByTitle
    - title attribute
  8. getByTestId
    -
    data-testid
    attribute (last resort)
Prefer
getByRole
over
getByTestId
. It tests accessibility too.
  1. getByRole
    - 匹配可访问角色(button, textbox, heading, checkbox等)
  2. getByLabelText
    - 关联了label的表单字段
  3. getByPlaceholderText
    - 输入框占位符
  4. getByText
    - 可见文本内容
  5. getByDisplayValue
    - input/select/textarea的当前值
  6. getByAltText
    - 图片alt文本
  7. getByTitle
    - title属性
  8. getByTestId
    -
    data-testid
    属性(最后备选方案)
优先使用
getByRole
而非
getByTestId
,它同时还能测试可访问性。

Wrapping with Providers

使用Providers包裹

jsx
// Enzyme with context:
const wrapper = mount(
  <ApolloProvider client={client}>
    <ThemeProvider theme={theme}>
      <MyComponent />
    </ThemeProvider>
  </ApolloProvider>
);

// RTL equivalent (use your project's customRender or wrap inline):
import { render } from '@testing-library/react';
render(
  <MockedProvider mocks={mocks} addTypename={false}>
    <ThemeProvider theme={theme}>
      <MyComponent />
    </ThemeProvider>
  </MockedProvider>
);
// Or use the project's customRender helper if it wraps providers
jsx
// Enzyme with context:
const wrapper = mount(
  <ApolloProvider client={client}>
    <ThemeProvider theme={theme}>
      <MyComponent />
    </ThemeProvider>
  </ApolloProvider>
);

// RTL equivalent (use your project's customRender or wrap inline):
import { render } from '@testing-library/react';
render(
  <MockedProvider mocks={mocks} addTypename={false}>
    <ThemeProvider theme={theme}>
      <MyComponent />
    </ThemeProvider>
  </MockedProvider>
);
// Or use the project's customRender helper if it wraps providers