React 测试库清理在 Jest 描述块中不起作用

React Testing Library cleanup not working in Jest's describe bocks

我有一些正在进行的测试,这是按预期工作的:

describe("Parent", () => {
  afterEach(() => {
    cleanup();
    jest.resetModules();
  });

  describe("Test 1", () => {
    const wrapper = render(
      <MockProvider>
        <MyComponent />
      </MockProvider>
    );

    test("1 ", () => {
      expect(wrapper.baseElement).toMatchSnapshot();
      expect(wrapper.getByText("Apply").disabled).toBe(true);
    });
  });

  describe("Test 2", () => {
    test("1 ", () => {
      const wrapper = render(
        <MockProvider>
          <MyComponent />
        </MockProvider>
      );
      console.log(wrapper.getByText("Apply").disabled);
      expect(1).toBe(1);
    });
  });
});

然而,当我将第二个渲染函数移出测试时出现错误:

describe("Parent", () => {
  afterEach(() => {
    cleanup();
    jest.resetModules();
  });

  describe("Test 1", () => {
    const wrapper = render(
      <MockProvider>
        <MyComponent />
      </MockProvider>
    );

    test("1 ", () => {
      expect(wrapper.baseElement).toMatchSnapshot();
      expect(wrapper.getByText("Apply").disabled).toBe(true);
    });
  });

  describe("Test 2", () => {
    const wrapper = render(
      <MockProvider>
        <MyComponent />
      </MockProvider>
    );
    test("1 ", () => {
      console.log(wrapper.getByText("Apply").disabled);
      expect(1).toBe(1);
    });
  });
});

我得到的错误是

Found multiple elements with the text: Apply

我在控制台中看到该组件被渲染了两次,所以我认为关于 describe 块的清理功能一定没有正常工作。这很奇怪,因为我们已经进行了酶测试,并且设置和拆卸工作正常。

我 运行 正在解决一个单独的问题,但在中间找到了 你的 问题的可能答案。

Jest describe 在任何测试 运行 之前按顺序阻塞 运行。 Source

因此,除了变量作用域之外,您基本上不应该在描述块中执行代码。如果您需要设置一个变量或呈现一个跨多个测试使用的模拟,请将其放在生命周期方法中,例如 beforeAllbeforeEach.

要理解这一点,我们需要稍微了解一下 Jest 运行 我们的测试以及 React Testing Library 如何渲染我们的组件。


开玩笑

考虑下面的代码并尝试猜测输出是什么:

describe('First describe', () => {
  console.log('First describe');

  it('First test', () => {
    console.log('First test');
  });
});

describe('Second describe', () => {
  console.log('Second describe');

  it('Second test', () => {
    console.log('Second test');
  });
});

输出(悬停查看):

先描述一下
第二个描述
第一次测试
第二次测试

请注意,所有 describe 方法都在测试开始之前初始化为 运行。

这应该已经让您对问题有所了解,但现在让我们看看 RTL。


React 测试库

考虑下面的代码并尝试猜测 DOM 在控制台中的样子:

const Greeting = () => 'Hello world';

describe('First describe', () => {
  const wrapper = render(<Greeting />);

  it('First test', () => {
    console.log(wrapper.debug());
  });
});

describe('Second describe', () => {
  render(<Greeting />);
});

输出(悬停查看):

<code><body> <div>Hello world</div> <div>Hello world</div> </body>

当我们不为 render 函数指定基本元素时,它总是使用相同的 document.body

当我们未指定自定义容器时,<div> 元素包装 Hello world 由 RTL 添加。

默认情况下所有 RTL 查询都绑定到基本元素--document.body 在这种情况下。

因此,

getByText('Hello world'); // will find two elements and throw


这就是 code in RTL 对于 render 函数的样子。 (半伪代码)

if(!baseElement) {
  baseElement = document.body // body will be shared across renders
}
if(!container) {
  baseElement.appendChild(document.createElement('div')) // wraps our component
}
ReactDOM.render(component, container)

return {  container, baseElement, ...getQueriesForElement(baseElement)  }


解决这个问题

执行以下操作之一:

  1. ittest 方法中调用 render
  2. 在查询中指定 container
  3. 为每个 render
  4. 指定不同的基本元素

另一个快速修复方法是在将组件传递给 render()

之前用“React.Fragment”包装您的组件
test ('should find text in <Component />', () => {
   const {getByText} = render (<><Component /></>)
   const divElement = getByText (/Find Text/i)
   expect (divElement).toBeInTheDocument ()
})