购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.3 使用MemoryRouter进行单元测试

问题

我们在React应用程序中使用路由,这样我们就可以使用更多浏览器的功能。我们可以给页面添加书签,在应用程序中创建深度链接,以及在历史记录中执行前进或者后退。

然而,一旦使用了路由,我们就让组件依赖于它自身之外的东西:浏览器地址。这似乎不是一个太大的问题,但确实会有影响。

假设我们想对一个关联路由的组件进行单元测试。例如,我们为2.2节的About组件创建一个单元测试

这个单元测试渲染组件,然后检查是否可以在输出中找到名称“Kip Russel”。当运行这个测试时,我们会得到以下错误信息:

错误发生的原因是NavLink在组件树中找不到Router。这意味着在测试之前,我们需要将组件包装在Router中。

此外,当About组件被挂载到特定的路由上时,我们可能会希望编写一个单元测试来检查这个组件是否正常工作。如果我们提供了一个Router组件,那么要如何模拟特定的路由呢?

这不仅是单元测试的问题。如果使用像Storybook这样的工具库 ,我们可能希望展示组件在被挂载到给定路径上时具体的视觉样式。

我们需要的Router类似于实际意义上的BrowserRouter,但该Router允许我们指定它的行为。

解决方案

react-router-dom库就提供了这样一个Router:MemoryRouter。MemoryRouter使用起来就像BrowserRouter一样。不同之处在于,BrowserRouter是底层浏览器历史记录API的接口,而MemoryRouter没有这种依赖关系。它可以跟踪当前地址,也可以在历史记录中前后移动,但这是通过简单的内存结构实现的。

让我们再看看前面那个失败的单元测试。我们把它包裹在MemoryRouter中,而不只是渲染About组件:

现在,当我们运行测试时,它正常工作。这是因为MemoryRouter将一个模拟版本的API注入了context中。这使得它的所有子组件都可以使用它。About组件现在可以渲染Link或Route,因为历史记录是可用的。

但MemoryRouter还有一个额外的优势。因为它模拟了浏览器历史记录API,所以可以使用initialEntries属性给它提供一个完全模拟的历史记录。initialEntries属性的值应该设置为一个历史记录条目的数组。如果设置为单个值的数组,它将被解释为当前地址。这允许你编写单元测试来检查组件在被挂载到给定路由上时的行为:

因为我们是在真实的浏览器中做测试,所以我们可以在Storybook中使用一个真正的BrowserRouter,但是MemoryRouter也允许我们模拟当前地址,就像我们在Storybook的ToAboutOffices中所做的那样(如图2-6所示)。

图2-6:我们可以用MemoryRouter模拟 /about/offices 路由

讨论

路由组件可以让你将你想要去的地方和你如何到达那里的具体细节分离开。在这个解决方案中,我们看到了分离的一个好处:可以创建一个模拟的浏览器地址来检查组件在不同路由上的行为。这也允许你在应用可以正常使用路由的前提下,更改路由的具体行为。如果你的应用程序不是单页面应用而是服务端渲染应用,那么将BrowserRouter替换为StaticRouter。调用浏览器的历史记录API的链接,也会变为浏览器原生的页面请求链接。路由是将策略(想做什么)和机制(如何做到)分离的一个很好的例子。

你可以从GitHub网站( https://oreil.ly/1NW8e )下载本解决方案的源代码。 a446i+o1IjXzUTUvRIliLNe8+S19JrH3fNdBH4O0zv2wCYCC5p4/LWSAIKDwtyP8

点击中间区域
呼出菜单
上一章
目录
下一章
×