测试时无法使用MemoryRouter导航到路径。

问题描述 投票:0回答:1

我紧紧地跟着例子走,但我无法获得 记忆路由器 (这就是你应该测试路由组件的方式吗?)使用jest和enzyme进行测试。

我想导航到其中一个路由,并在我的快照中反映出来。下面的代码试图使用MemoryRouter导航到 "A",所以我假设我会看到 <div>A</div>

import React from 'react';
import Enzyme, {mount} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import {BrowserRouter as Router, MemoryRouter, Route, Switch} from 'react-router-dom';

Enzyme.configure({adapter: new Adapter()});

describe('Routing test', () => {
    let wrapper;

    beforeEach(() => {
        wrapper = mount(
            <MemoryRouter initialEntries={["/A"]}>
                    <div className={"Test"}>This is my Test Component and should not have any test specific code in it
                        <Router>
                            <Switch>
                                <Route path={"/A"}>
                                    <div className={"A"}>A</div>
                                </Route>
                                <Route path={"/B"}>
                                    <div>B</div>
                                </Route>
                            </Switch>
                        </Router>
                    </div>
                </MemoryRouter>
        );
    });
    afterEach(() => {
        wrapper.unmount();
    });

    it('matches snapshot', () => {
        expect(wrapper.find(".Test")).toHaveLength(1); //this ok
        expect(wrapper.find(".A")).toHaveLength(1); //but this is not ok :( It should find  A
    });
});

而不是看到 <div>Test<div>A</div></div> 我只是看到 <div>Test</div>

注意:我的例子是简化成一个类。 我的例子被简化为一个类。我的实际情况是 <div>Test...</div> 是一个独立的组件。

react-router jestjs enzyme
1个回答
2
投票

根据 文件如果你使用普通的 Router 在你的测试中,你应该通过 history

虽然你可能会想自己把路由器的上下文抠出来,但我们建议你把你的单元测试包在Router组件中:基础的 Router 与历史道具,或 <StaticRouter>, <MemoryRouter><BrowserRouter>

希望这个能用。如果不行,也许用第二个 MemoryRouter 而不是 Router 会简单地做工作。


1
投票
  1. 我找不到任何证据,但我总是在印象中,比你应该只使用一个。<Router> 在树顶的某个地方,不应该嵌套它们。所以我自己在源码里看了一下,如果我没看错的话,这是真的。因为

    1. react -router 用途 上下文API 来向下传递道具。
    2. React 文件:

      [......]它将从当前的上下文值中读取 最接近 相配的 Provider 上面它在树上。

    3. <Router> 是一个 Provider 而非 Consumer所以它不能偷看父体的道具。<Router>
  2. 当人们提倡测试的时候,他们也会提到写测试会导致更多的可测试的代码,而更多的可测试的代码会更干净。我不会争论这个问题,我只是想指出,如果你能写出一个可测试的代码,那么你也可以写出一个不可测试的代码。而这看起来是这样的。

    所以尽管你特别说

    不应包含任何测试专用代码

    我想说的是,虽然你可能不应该使用 createMemoryHistory 按照@aquinq的建议,或者把其他的东西具体和 试验用 的目的,你可以而且可能应该 修改 你的代码更容易被测试。

    你可以

    1. 移动 <Router> 更高。你甚至可以把 <App> 与它--这是最简单的,也是值得推荐的方法,虽然可能不适用你的情况。但我还是不明白为什么你不能将 <div className={"Test"}> 里面 <Router> 而不是反之。
    2. 在你的测试中,你不应该测试第三方库,你应该测试你自己的代码,所以你可以提取这个
      <Switch>
        <Route path={"/A"}>
          <div className={"A"}>A</div>
        </Route>
        <Route path={"/B"}>
          <div>B</div>
        </Route>
      </Switch>
      
      部份放入一个单独的组件中,分别进行测试。
    3. 或者如果我们把这两者结合起来:把 <div className={"Test"}> 里面 <Router>萃取 <div className={"Test"}> 变成一个单独的组件,写上
      wrapper = mount(
        <MemoryRouter initialEntries={["/A"]}>
          <TestDiv/>
        </MemoryRouter>
      )
      
    4. 另外 createMemoryHistory 本身就可以成为一个有用的功能。而在未来的某一天,你会发现自己在使用它。在这种情况下,@aquinq的回答就可以了。
  3. 但如果你根本不想修改你的代码。那么你可以欺骗一下,试试这个方法。如何测试一个带有<Router> 标签的组件?


1
投票

OK,我想明白了。

它非常丑陋,但你需要创建一个新的组件。__mocks__ 目录下(在你项目的第一层)。__mocks__ 似乎没有什么文档,但它似乎是一个玩笑,这里的所有东西都将在测试时运行,这里你可以为某些外部库添加mock stubs。

import React from 'react';

const reactRouterDom = require("react-router-dom")
reactRouterDom.BrowserRouter = ({children}) => <div>{children}</div>

module.exports = reactRouterDom

我的测试文件和我的问题一样(我想)。

import React from 'react';
import Enzyme, {mount} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import {BrowserRouter as Router, MemoryRouter, Route, Switch} from 'react-router-dom';

Enzyme.configure({adapter: new Adapter()});

describe('Routing test', () => {
    let wrapper;

    beforeEach(() => {
        wrapper = mount(
            <MemoryRouter initialEntries={['/A']}>
                    <div className={"Test"}>This is my Test Component and should not have any test specific code in it
                        <Router>
                            <Switch>
                                <Route path={"/A"}>
                                    <div className={"A"}>A</div>
                                </Route>
                                <Route path={"/B"}>
                                    <div>B</div>
                                </Route>
                            </Switch>
                        </Router>
                    </div>
                </MemoryRouter>
        );
    });
    afterEach(() => {
        wrapper.unmount();
    });

    it('matches snapshot', () => {
        expect(wrapper.find(".Test")).toHaveLength(1); //this ok
        expect(wrapper.find(".A")).toHaveLength(1); //but this is not ok :( It should find  A
    });
});

这可以工作,我的测试是绿色的! :)

更新:

我想我有点糊涂了,因为我把Router当做其他react组件,而实际上它是一个像redux一样的顶层组件。Provider. 路由器不应该在室内。App 却在 App 如是 例如,index.js 文件为例)。)

ReactDOM.render(
    <Provider store={store}>
        <Router>
            <App/>,
        </Router>
    </Provider>,
    document.getElementById('root')
);

现在在针对App编写测试时,我提供了自己的路由器,如 记忆路由器.


1
投票

通常Router会在应用逻辑之外,如果你在使用其他的 <Route> 标签,那么您可以使用类似 <Switch>,像这样。

  <Router>
    <Switch>
      <Route exact path="/">
        <HomePage />
      </Route>
      <Route path="/blog">
        <BlogPost />
      </Route>
    </Switch>
  </Router>

MemoryRouter 其实 a Router因此,最好的办法是 替换 真人 Router 这里。 你可以把它拆成一个单独的组件,以便于测试。

根据 源自GitHub:

最常见的使用情况是使用低级的 <Router> 是将自定义的历史记录与Redux或Mobx等状态管理lib同步。需要注意的是,在React Router旁边使用状态管理lib并不是必须的,这只是为了深度集成。

import React from "react";
import ReactDOM from "react-dom";
import { Router } from "react-router";
import { createBrowserHistory } from "history";

const history = createBrowserHistory();

ReactDOM.render(
  <Router history={history}>
    <App />
  </Router>,
  node
);

根据个人经验。

我曾使用过一个外部组件(我们称之为 "Root"),其中包括 <Provider><Router> 在顶层的组件,然后是 <App> 仅包括 <Switch><Route> 组件。

Root.jsx 返回。

<Provider store={rootStore}>
  <Router history={rootHistory}>
    <App />
  </Router>
</Provider>

App.jsx 返回。

<Switch>
  <Route exact path="/" component={HomePage}>
  <Route exact path="/admin" component={AdminPage}>
</Switch>

这允许 App.test.jsx 来使用。

mount(
  <Provider store={fakeStore}>
    <MemoryRouter initialEntries={['/']}>
      <App myProp={dummyProp} />
    </MemoryRouter>
  </Provider>
)
© www.soinside.com 2019 - 2024. All rights reserved.