问题描述
我正在我的项目中使用react-testing-library,并试图编写可验证应用内路由的测试.
I'm using react-testing-library within a project of mine and am trying to write tests that validate in-app routing.
例如测试AccessDenied页面上的按钮是否会将您带回到主页.
e.g. testing that a button on the AccessDenied page brings you back to the Home page.
我已经能够为我的App组件成功编写这些类型的测试,因为它定义了所有的App路由.但是,如果AccessDenied 是其中的一条路线,我该如何设置我的测试以验证单击的按钮将把我返回首页?
I've been able to write these sorts of tests successfully for my App component because it defines all of the app routes. But if AccessDenied is one of those routes, how do I need to set up my tests to validate a button clicked there will route my back to Home?
这是一个人为的例子:
App.tsx
<>
<Router>
<Route exact path="/" component={Home} />
<Route exact path="/access-denied" component={AccessDenied} />
</Router>
<Footer />
</>
AccessDenied.tsx
AccessDenied.tsx
<div>
<div>Access Denied</div>
<p>You don't have permission to view the requested page</p>
<Link to="/">
<button>Go Home</button> <--- this is what i want tested
</Link>
</div>
正如我之前说的,我的测试在App.test.tsx
之内工作的原因是因为我的App组件定义了其内部的路由,而我的AccessDenied
只是这些路由之一.但是,是否可以在AccessDenied.test.tsx
测试中利用在App.tsx
中定义的路由器?也许我正在错误地解决这个问题?那就是我在努力的地方.作为参考,这是我正在工作的App.test.tsx
测试.
As I said earlier the reason my tests work inside App.test.tsx
is because my App component defines the routes inside itself, whereas my AccessDenied
is just one of those routes. However, is it possible to leverage the router defined in my App.tsx
in my AccessDenied.test.tsx
tests? Perhaps I'm approaching this problem incorrectly? That's where I'm struggling. For reference, here is my working App.test.tsx
tests.
App.test.tsx
App.test.tsx
describe('App', () => {
it('should allow you to navigate to login', async () => {
const history = createMemoryHistory()
const { findByTestId, getByTestId } = render(
<MockedProvider mocks={mocks} addTypename={false}>
<AuthContext.Provider
value={{
authState: AUTH_STATES.UNAUTHENTICATED,
}}
>
<Router history={history}>
<App />
</Router>
</AuthContext.Provider>
</MockedProvider>,
)
fireEvent.click(getByTestId('sidebar-login-button'))
expect(await findByTestId('login-page-login-button')).toBeInTheDocument()
fireEvent.click(getByTestId('login-page-register-button'))
expect(await findByTestId('register-page-register-button')).toBeInTheDocument()
})
})
任何想法或建议都值得赞赏!
Any thoughts or suggestions are appreciated!
推荐答案
如果您考虑AccessDenied
组件的职责,实际上并没有向用户发送 home .这就是您想要的整体行为,但是组件的作用仅仅是将用户发送到"/"
.因此,在组件单元级别,测试可能看起来像这样:
If you think about the responsibility of the AccessDenied
component, it isn't really to send the user home. That's the overall behaviour you want, but the component's role in that is simply to send the user to "/"
. At the component unit level, therefore, the test could look something like this:
import React, { FC } from "react";
import { Link, Router } from "react-router-dom";
import { fireEvent, render, screen } from "@testing-library/react";
import { createMemoryHistory } from "history";
const AccessDenied: FC = () => (
<div>
<div>Access Denied</div>
<p>You don't have permission to view the requested page</p>
<Link to="/">
<button>Go Home</button>
</Link>
</div>
);
describe("AccessDenied", () => {
it("sends the user back home", () => {
const history = createMemoryHistory({ initialEntries: ["/access-denied"] });
render(
<Router history={history}>
<AccessDenied />
</Router>
);
fireEvent.click(screen.getByText("Go Home"));
expect(history.location.pathname).toBe("/");
});
});
请注意,"/"
是默认路径,因此,如果您不提供initialEntries
,即使点击没有任何作用,测试也会通过...
Note that "/"
is the default path, so if you don't provide initialEntries
the test passes even if the click doesn't do anything...
到那时,您可能会想",但是如果家庭路线更改了该怎么办?" 例如,如果您将主页移动到"/home"
,则该测试将继续通过,但是该应用程序将不再实际运行.这是过分依赖非常低级测试的常见问题,并且是高阶测试发挥作用的地方,其中包括:
At that point you might be thinking "but what if the home route changes?" If you moved the home page to "/home"
, for example, this test would continue to pass but the application would no longer actually work. This is a common problem with relying too much on very low-level tests and is where higher-level tests come into play, including:
-
集成:渲染整个
App
并使用fireEvent
模拟导航.在当前的设置中,这很有挑战性,因为Router
处于App
级别.我将Router
移至index.tsx
并在App.tsx
中使用了Switch
,因此您可以在MemoryRouter
中渲染App
或使用上面显示的createMemoryHistory
方法(我已经例如,在此入门工具包中完成了此操作.
Integration: render the whole
App
and usefireEvent
to simulate navigation. This is challenging in your current setup, because theRouter
is at theApp
level; I'd move theRouter
toindex.tsx
and have aSwitch
inApp.tsx
instead, so you can renderApp
within aMemoryRouter
or use thecreateMemoryHistory
method I show above (I've done this in this starter kit for example).
端到端:使用浏览器驱动程序(例如Cypress或各种基于Selenium的选项)自动实现用户与应用程序的实际交互.
End-to-end: use a browser driver (e.g. Cypress or the various Selenium-based options) to automate actual user interactions with the app.
我还没有展示路由测试,但确实涵盖了一个简单的React应用的这些不同级别的测试在我的博客上.
I haven't got as far as showing tests for routing, but do cover these different levels of test for a simple React app on my blog.
这篇关于在React-Router的路由中进行基于路由的测试的推荐方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!