我紧紧地跟着例子走,但我无法获得 记忆路由器 (这就是你应该测试路由组件的方式吗?)使用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>
是一个独立的组件。
根据 文件如果你使用普通的 Router
在你的测试中,你应该通过 history
榰
虽然你可能会想自己把路由器的上下文抠出来,但我们建议你把你的单元测试包在Router组件中:基础的
Router
与历史道具,或<StaticRouter>
,<MemoryRouter>
或<BrowserRouter>
希望这个能用。如果不行,也许用第二个 MemoryRouter
而不是 Router
会简单地做工作。
我找不到任何证据,但我总是在印象中,比你应该只使用一个。<Router>
在树顶的某个地方,不应该嵌套它们。所以我自己在源码里看了一下,如果我没看错的话,这是真的。因为
[......]它将从当前的上下文值中读取 最接近 相配的
Provider
上面它在树上。
<Router>
是一个 Provider
而非 Consumer
所以它不能偷看父体的道具。<Router>
当人们提倡测试的时候,他们也会提到写测试会导致更多的可测试的代码,而更多的可测试的代码会更干净。我不会争论这个问题,我只是想指出,如果你能写出一个可测试的代码,那么你也可以写出一个不可测试的代码。而这看起来是这样的。
所以尽管你特别说
不应包含任何测试专用代码
我想说的是,虽然你可能不应该使用 createMemoryHistory
按照@aquinq的建议,或者把其他的东西具体和 试验用 的目的,你可以而且可能应该 修改 你的代码更容易被测试。
你可以
<Router>
更高。你甚至可以把 <App>
与它--这是最简单的,也是值得推荐的方法,虽然可能不适用你的情况。但我还是不明白为什么你不能将 <div className={"Test"}>
里面 <Router>
而不是反之。<Switch>
<Route path={"/A"}>
<div className={"A"}>A</div>
</Route>
<Route path={"/B"}>
<div>B</div>
</Route>
</Switch>
部份放入一个单独的组件中,分别进行测试。<div className={"Test"}>
里面 <Router>
萃取 <div className={"Test"}>
变成一个单独的组件,写上wrapper = mount(
<MemoryRouter initialEntries={["/A"]}>
<TestDiv/>
</MemoryRouter>
)
createMemoryHistory
本身就可以成为一个有用的功能。而在未来的某一天,你会发现自己在使用它。在这种情况下,@aquinq的回答就可以了。但如果你根本不想修改你的代码。那么你可以欺骗一下,试试这个方法。如何测试一个带有<Router> 标签的组件?
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编写测试时,我提供了自己的路由器,如 记忆路由器.
通常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>
)