使用 react-query 和 axios 在 React 组件中测试错误的问题。使用 React 测试库、Jest、msw 进行测试

Problem with testing for error in React component using react-query and axios. Test with React Testing Library, Jest, msw

我测试了一个应该显示微调器、错误或获取的食谱列表的反应组件。我设法测试了这个组件的加载和成功获取数据的时间。我有测试错误的问题。 我用 msw 创建了测试服务器,它有 returns 错误的路由处理程序。我使用 axios 向服务器发出请求。我认为问题出在这里:axios 发出 3 个请求,直到最后一个响应 useQuery returns isLoading=true, isError=false。只有在它 returns isLoading=false, isError=true 之后。所以在我的错误 screen.debug() 测试中显示微调器,而 errorMessage returns 错误是因为它没有找到带有文本 'error' 的呈现元素,该组件应该在发生错误时显示。我该怎么办?我运行没主意了。


编辑:


我是 React 测试和 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} />
      ))}
    </>
  );
};

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

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

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

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

“test.Use jest.setTimeout(newTimeout) 超时超过 5000 毫秒,如果这是一个长 运行 测试,则增加超时值。” 我不得不更改 Jest 为测试设置的超时值。 可以通过 comment:

中描述的两种方式完成

" 1. 向 it 添加第三个参数。即 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 } 对象作为参数。

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

我使用 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();
    })
  });