【问题标题】:Problem with testing for error in React component using react-query and axios. Test with React Testing Library, Jest, msw使用 react-query 和 axios 测试 React 组件中的错误的问题。使用 React 测试库、Jest、msw 进行测试
【发布时间】:2021-07-27 20:53:54
【问题描述】:

我测试了一个反应组件,它应该显示微调器、错误或获取的配方列表。我设法测试了这个组件的加载以及它何时成功获取数据。我有测试错误的问题。 我用 msw 创建了测试服务器,它具有返回错误的路由处理程序。我使用 axios 向服务器发出请求。我认为问题出在这里:axios 发出 3 个请求,直到最后一个响应 useQuery 返回 isLoading=true,isError=false。只有在那之后它才返回 isLoading=false, isError=true。因此,在我对错误 screen.debug() 的测试中,显示微调器,并且 errorMessage 返回错误,因为它没有找到带有文本“错误”的渲染元素,该组件应该在发生错误时显示。我能做些什么呢?我的想法已经用完了。


编辑:

  • 我发现 useQuery 中有一个设置“重试”,默认为 3 个请求。我仍然不知道如何处理我的测试中的组件重试请求。

我是反应测试和 Typescript 的新手。

来自RecipeList.test.tsx:

import { setupServer } from 'msw/node';
import { rest } from 'msw';
import { QueryClient, QueryClientProvider, QueryCache } from 'react-query';

import RecipeList from './RecipeList';

const server = setupServer(
  rest.get(`${someUrl}`, (req, res, ctx) =>
    res(ctx.status(200), ctx.json(data))
  )
);

const queryCache = new QueryCache();
const RecipeListWithQueryClient = () => {
  const client = new QueryClient();
  return (
    <QueryClientProvider client={client}>
      <RecipeList />
    </QueryClientProvider>
  );
};

// Tests setup
beforeAll(() => server.listen());
afterAll(() => server.close());
afterEach(() => server.resetHandlers());
afterEach(() => {
  queryCache.clear();
});

describe('RecipeList', () => {
  //some tests that pass

  describe('while error', () => {
    it('display error message', async () => {
      server.use(
        rest.get(`${someUrl}`, (req, res, ctx) => res(ctx.status(404)))
      );
      render(<RecipeListWithQueryClient />);
      // FOLLOWING CODE RAISE ERROR
      const errorMessage = await screen.findByText(/error/i);
      await expect(errorMessage).toBeInTheDocument();
    });
  });
});

组件代码:

来自 RecipeList.tsx:

import { fetchRecipes } from 'api';

const RecipeList: React.FC = () => {
  const { data, isLoading, isError } = useQuery<
    RecipeDataInterface,
    Error
  >('recipes', fetchRecipes);;

  if (isLoading) {
    return (
      <Spinner>
        <span className="sr-only">Loading...</span>
      </Spinner>
    );
  }

  if (isError) {
    return (
      <Alert >Error occured. Please try again later</Alert>
    );
  }

  return (
    <>
      {data?.records.map((recipe: RecipeInterface) => (
        <RecipeItem key={recipe.id} recipe={recipe.fields} />
      ))}
    </>
  );
};

【问题讨论】:

    标签: reactjs typescript mocking react-testing-library msw


    【解决方案1】:

    我找到的解决方案是为测试设置单独的超时值。

    根据docs 在 RTL 中为 waitFor 异步方法:

    “默认超时为 1000 毫秒,这将使您处于 Jest 的默认超时 5000 毫秒之下。”

    我必须为 RTL 异步函数设置超时选项,但这还不够,因为我收到了一个错误:

    “超过 5000 毫秒的测试超时。如果这是一个长时间运行的测试,请使用 jest.setTimeout(newTimeout) 增加超时值。” 我不得不更改 Jest 为测试设置的超时值。 可以通过comment 中描述的两种方式完成:

    " 1. 给它添加第三个参数。即it('runs slow', () => {...}, 10000) 2.写jest.setTimeout(10000);在名为 src/setupTests.js 的文件中,如此处所指定。"

    最后我的测试是这样的:

    
      it('display error message', async () => {
        server.use(
          rest.get(getUrl('/recipes'), (req, res, ctx) =>
            res(ctx.status(500), ctx.json({ errorMessage: 'Test Error' }))
          )
        );
    
        render(<RecipeListWithQueryClient />);
    
        expect(
          await screen.findByText(/error/i, {}, { timeout: 10000 })
        ).toBeInTheDocument();
      }, 10000);
    

    这里可以使用不同的异步方法,例如 waitFor 和 waitForElementToBeRemoved,它们都可以将 { timeout: 10000 } 对象作为参数。

    这个解决方案对我有用,但我想改进它,以避免测试花费太多时间。我只是还没弄清楚怎么做。

    【讨论】:

      【解决方案2】:

      我使用 QueryClient 禁用重试。 和 setupServer 模拟网络连接

          import { QueryClient, QueryClientProvider} from 'react-query';
          import { setupServer } from 'msw/node'
          import { rest } from 'msw'
      
          
      const server = setupServer( 
        rest.get('https://reqres.in/api/users', (req, res, ctx) => {      
            return res(
              ctx.status(200),
              ctx.json(userSuccessResponseJson()
                  ),
            )
          }),)
      
      beforeAll(() => server.listen())
      afterEach(() => {
        server.resetHandlers();
        queryClient.clear();
      })
      afterAll(() => server.close())
      
      
      const queryClient = new QueryClient({
        defaultOptions: {
          queries: {
            retryDelay: 1,
            retry:0,
          },
        },
      })
      const Wrapper = ({ children }) => (
        <QueryClientProvider client={queryClient}>
          {children}
        </QueryClientProvider>
      );
      
      
      
        test('Renders Error text when backend server response with error', async () => {
          server.use(
            rest.get('https://reqres.in/api/users', (req, res, ctx) => {
              return res(ctx.status(403))
            })
          );
        
          render(<Wrapper><CompanyList />)</Wrapper>);
        
          const linkElement = screen.getByText(/Loading/i);
          expect(linkElement).toBeInTheDocument();
        
          await waitFor(() => {      
            const fetchedText = screen.getByText(/Error:/i);      
            expect(fetchedText).toBeInTheDocument();
          })
        });
      

      【讨论】:

      • 谢谢,这就是我要找的!
      • 这就是答案
      猜你喜欢
      • 2021-06-04
      • 2020-03-24
      • 2021-11-30
      • 2018-08-20
      • 2019-02-24
      • 2021-05-27
      • 2021-06-24
      • 1970-01-01
      相关资源
      最近更新 更多