react-native-testing-library:如何使用 act 测试 useEffect

Posted

技术标签:

【中文标题】react-native-testing-library:如何使用 act 测试 useEffect【英文标题】:react-native-testing-library: how to test useEffect with act 【发布时间】:2020-03-26 14:31:39 【问题描述】:

我正在使用 react-native-testing-library 来测试我的 react-native 组件。 我有一个组件(为了这篇文章的目的,它已经被过度简化了):

export const ComponentUnderTest = () => 

 useEffect(() => 
   __make_api_call_here_then_update_state__
 , [])

 return (
   <View>
     __content__goes__here
   </View>
 )
 

这是我的(简化版)component.spec.tsx

import  render, act  from 'react-native-testing-library';
import  ComponentUnderTest  from './componentundertest.tsx';

test('it updates content on successful call', () => 
   let root;
   act(() => 
      root = render(<ComponentUnderTest />); // this fails with below error message
   );    
   expect(...);
)

现在当我运行这段代码时,我得到了这个错误: Can't access .root on unmounted test renderer

我什至现在都不知道这个错误消息是什么意思。我按照react-native-testing-library 中的文档了解如何使用act and useEffect 进行测试。

任何帮助将不胜感激。谢谢

【问题讨论】:

【参考方案1】:

我找到了解决方法:

import  render, waitFor  from 'react-native-testing-library';
import  ComponentUnderTest  from './componentundertest.tsx';

test('it updates content on successful call', async () => 
   const root = await waitFor(() =>
       render(<ComponentUnderTest />);
   );   
   expect(...);
)

【讨论】:

这不行,时间过去了,什么也没有发生。【参考方案2】:
root = render(<ComponentUnderTest />);

应该是

 root = create(<ComponentUnderTest />);

----全码sn-p。经过上述更改后它对我有用

import React,  useState, useEffect  from 'react'
import  Text, View  from 'react-native'
import  render, act  from 'react-native-testing-library'
import  create  from 'react-test-renderer'

export const ComponentUnderTest = () => 
  useEffect(() => , [])

  return (
    <View>
      <Text>Hello</Text>
    </View>
  )


test('it updates content on successful call', () => 
  let root
  act(() => 
    root = create(<ComponentUnderTest />) 
  )
)

【讨论】:

感谢您的回答。但是您从哪个库导入createreact-native-testing-library好像没有这样的导出成员 react-test-renderer(它已经是 react-native-testing-library 的依赖项) 我按照你的建议使用了create。不幸的是,我遇到了同样的错误。一些类似的错误/问题(在 react,不是 react-native,具有相应的 @testing-library/react)报告了未匹配版本的问题。 (见github.com/testing-library/react-hooks-testing-library/issues/…)我不知道什么是适合我的版本 我已经用完整的代码更新了我的答案。它在我使用 create 时有效。使用渲染我得到同样的错误 失败了不是吗?我们希望渲染使用查询:getByRolegetByTestID 等。除非有其他方法可以找到使用 fireEvents 的元素,否则在这种情况下我看不到 create 有多大用处。我在create 上也找不到太多文档或示例。【参考方案3】:

您可以使用:@testing-library/react-native

例子:

import  cleanup, fireEvent, render, debug, act from '@testing-library/react-native'

afterEach(() => cleanup());

test('given correct credentials, gets response token.', async () => 
    const  debug, getByPlaceholderText, getByRole  = await render(<Component/>);

    await act( async () => 
            const emailInput = getByPlaceholderText('Email');;
            const passwordInput = getByPlaceholderText('Password');
            const submitBtn = getByRole('button', name: '/submitBtn/i');

            fireEvent.changeText(emailInput, 'email');
            fireEvent.changeText(passwordInput, 'password');
            fireEvent.press(submitBtn);
    );
);

也应该与 useEffect 一起使用,但我自己还没有测试过。与 useState 一起工作正常。

【讨论】:

如果你只需要在第一次渲染后等待useEffect 被触发一次,你会用什么包裹act【参考方案4】:

以下步骤解决了我的情况:

升级Reactreact-test-renderer 版本到16.9 或更高版本,支持act 内部的async 函数(据我所知,这两个包需要是相同的版本)

李>

按照@helloworld 的建议,将react-native-testing-libraryrender 替换为react-test-renderercreate(谢谢您,先生,它帮助了我)

创建测试函数async,在act 前面加上await,并传递一个async 函数给它

最终结果如下所示:

test('it updates content on successful call', async () => 
  let root
  await act(async () => 
    root = create(<ComponentUnderTest />) 
  )
)

【讨论】:

你是如何与 getTestById 等结合起来的,因为 create 没有它们,我相信它很浅。 const getByTestId = await waitFor(() => return render(component); );【参考方案5】:

我使用useEffect 测试异步组件并触发setState 重新渲染的方法是正常设置测试用例,但使用waitFor or findBy 阻止断言,直到组件使用获取的重新渲染数据。

这是一个简单的可运行示例:

import React, useEffect, useState from "react";
import FlatList, Text from "react-native";
import render from "@testing-library/react-native";

const Posts = () => 
  const [posts, setPosts] = useState(null);

  useEffect(() => 
    const url = "https://jsonplaceholder.typicode.com/posts";
    fetch(url).then(res => res.json()).then(setPosts);
  , []);

  return !posts ? <Text>loading</Text> : <FlatList
    testID="posts"
    data=posts
    renderItem=(item: id, title, index) =>
      <Text testID="post" key=id>title</Text>
    
  />;
;

describe("Posts", () => 
  beforeEach(() => 
    global.fetch = jest.fn(url => Promise.resolve(
      ok: true,
      status: 200,
      json: () => Promise.resolve([
        id: 1, title: "foo title",
        id: 2, title: "bar title",
      ])
    ));
  );

  it("should fetch posts", async () => 
    const findAllByTestId = render(<Posts />);
    const posts = await findAllByTestId("post", timeout: 500);
    expect(posts).toHaveLength(2);
    expect(posts[0]).toHaveTextContent("foo title");
    expect(posts[1]).toHaveTextContent("bar title");
    expect(fetch).toHaveBeenCalledTimes(1);
  );
);

这并没有给我任何act 警告,但我已经分享了这些警告。 This open GitHub issue 似乎是规范资源。

使用的包:


  "dependencies": 
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-native": "^0.64.0",
    "react-native-web": "^0.15.6"
  ,
  "devDependencies": 
    "@babel/core": "^7.13.15",
    "@testing-library/jest-native": "^4.0.1",
    "@testing-library/react-native": "^7.2.0",
    "babel-jest": "^26.6.3",
    "jest": "^26.6.3",
    "metro-react-native-babel-preset": "^0.65.2",
    "react-test-renderer": "^17.0.2"
  

在 Jest 配置中:

setupFilesAfterEnv: ["@testing-library/jest-native/extend-expect"],

对于.toHaveTextContent 匹配器。或者您可以使用导入:

import "@testing-library/jest-native/extend-expect";

【讨论】:

以上是关于react-native-testing-library:如何使用 act 测试 useEffect的主要内容,如果未能解决你的问题,请参考以下文章