【问题标题】:React Testing Library cleanup not working in Jest's describe bocksReact 测试库清理在 Jest 的描述块中不起作用
【发布时间】:2019-11-27 14:19:06
【问题描述】:

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

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);
        });
    });
});

我得到的错误是

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

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

【问题讨论】:

  • 你为什么要这样渲染?
  • 在我看来,describe 块会设置 case,然后 test 会有断言。
  • 我不确定它是否有效。我认为每个渲染都必须是独立的,你不能重复使用它们

标签: jestjs react-testing-library


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    要理解这一点,我们需要稍微了解一下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。


    反应测试库

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

    const Greeting = () => 'Hello world';
    
    describe('First describe', () => {
      const wrapper = render(<Greeting />);
    
      it('First test', () => {
        console.log(wrapper.debug());
      });
    });
    
    describe('Second describe', () => {
      render(<Greeting />);
    });
    

    输出(悬停查看):

    &lt;body&gt; &lt;div&gt;Hello world&lt;/div&gt; &lt;div&gt;Hello world&lt;/div&gt; &lt;/body&gt;

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

    当我们不指定自定义容器时,包装 Hello world&lt;div&gt; 元素由 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指定一个不同的基本元素

    【讨论】:

    • 哇,你的回答正好符合我的需要,谢谢
    【解决方案3】:

    另一个快速解决方法是在将组件传递给render()之前用“React.Fragment”包装你的组件

    test ('should find text in <Component />', () => {
       const {getByText} = render (<><Component /></>)
       const divElement = getByText (/Find Text/i)
       expect (divElement).toBeInTheDocument ()
    })
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-17
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-26
      • 2019-06-21
      • 2020-03-24
      相关资源
      最近更新 更多