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

Eva*_*nss 14 jestjs react-testing-library

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

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);
        });
    });
});
Run Code Online (Sandbox Code Playgroud)

但是,当我将第二个渲染函数移出测试时,它会出错:

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);
        });
    });
});
Run Code Online (Sandbox Code Playgroud)

我得到的错误是

找到多个带有文本的元素:Apply

我可以在控制台中看到组件被渲染了两次,所以我认为清理功能对于描述块一定不能正常工作。这很奇怪,因为我们已经进行了酶测试,并且设置和拆卸对这些都很好。

Dou*_*oug 14

要理解这一点,我们需要了解一点关于如何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');
  });
});
Run Code Online (Sandbox Code Playgroud)

输出(悬停查看):

第一个描述
第二个描述
第一个测试
第二个测试

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

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


反应测试库

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

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

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

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

describe('Second describe', () => {
  render(<Greeting />);
});
Run Code Online (Sandbox Code Playgroud)

输出(悬停查看):

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

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

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

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

所以,

getByText('Hello world'); // will find two elements and throw
Run Code Online (Sandbox Code Playgroud)


这就是函数的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)  }
Run Code Online (Sandbox Code Playgroud)


为了解决这个问题

执行以下操作之一:

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

  • 这是一个很好的答案!如果每个建议的解决方案都有一个代码片段,那就太好了 (2认同)

Pau*_*est 5

我遇到了一个单独的问题,但在中间找到了问题的可能答案。

Jestdescribe块在任何测试运行之前按顺序运行。来源

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