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