Testing 测试

React Router relies on React context to work. This affects how you can test your components that use our components.

React Router依赖于React。这会影响你如何通过使用我们的组件来测试你的组件。

Context 上下文

If you try to unit test one of your components that renders a <Link> or a <Route>, etc. you’ll get some errors and warnings about context. While you may be tempted to stub out the router context yourself, we recommend you wrap your unit test in a <StaticRouter> or a <MemoryRouter>. Check it out:

如果你尝试单元测试你渲染的<Link>或者<Router>组件,你会得到一些上下文的错误和警告信息。虽然你可能会找出自己的路由上下文,我们推荐你将你的单元测试包裹在<StaticRouter>或者<MemoryRouter>中。来看看:

  1. class Sidebar extends Component {
  2. // ...
  3. render() {
  4. return (
  5. <div>
  6. <button onClick={this.toggleExpand}>
  7. expand
  8. </button>
  9. <ul>
  10. {users.map(user => (
  11. <li>
  12. <Link to={user.path}>
  13. {user.name}
  14. </Link>
  15. </li>
  16. ))}
  17. </ul>
  18. </div>
  19. )
  20. }
  21. }
  22. // broken
  23. test('it expands when the button is clicked', () => {
  24. render(
  25. <Sidebar/>
  26. )
  27. click(theButton)
  28. expect(theThingToBeOpen)
  29. })
  30. // fixed!
  31. test('it expands when the button is clicked', () => {
  32. render(
  33. <MemoryRouter>
  34. <Sidebar/>
  35. </MemoryRouter>
  36. )
  37. click(theButton)
  38. expect(theThingToBeOpen)
  39. })

That’s all there is to it. 这就是所有的示例。

这就是它的全部。

Starting at specific routes | 从特定的路由开始

<MemoryRouter> 支持initialEntriesinitialIndex 属性。,因此你可以从一个特定的地址(location)来启动你的app(或者是app的一个小部分)。

  1. test('current user is active in sidebar', () => {
  2. render(
  3. <MemoryRouter initialEntries={[ '/users/2' ]}>
  4. <Sidebar/>
  5. </MemoryRouter>
  6. )
  7. expectUserToBeActive(2)
  8. })

Navigating

当地址(location)发生变化的时候,我们会对路由进行大量的测试,所以你可能没有必要对这个(Navigating )进行测试。但是如果你必须这样做,基于一切都在渲染中发生,我们可以对此做一些聪明的事情:

导航

当地址变化时我们有许多测试路由的工作,因此你可能不需要测试这个东西。但是如果你必须这样做,一切发生在渲染的时候,我们可以聪明一点这样做:

  1. import { render, unmountComponentAtNode } from 'react-dom'
  2. import React from 'react'
  3. import { Route, Link, MemoryRouter } from 'react-router-dom'
  4. import { Simulate } from 'react-addons-test-utils'
  5. // 把整个app都放在一个MemoryRouter里面渲染的其中一个方法是
  6. // 把他们都放进一个要执行的步骤列表里面,
  7. // 当地址(location)发生变化的时候,
  8. // 它就会连同`match`, `location`, 和 `history`一起被回调,
  9. // 因此,你可以控制整个流程和做断言。
  10. const renderTestSequence = ({
  11. initialEntries,
  12. initialIndex,
  13. subject: Subject,
  14. steps
  15. }) => {
  16. const div = document.createElement('div')
  17. class Assert extends React.Component {
  18. componentDidMount() {
  19. this.assert()
  20. }
  21. componentDidUpdate() {
  22. this.assert()
  23. }
  24. assert() {
  25. const nextStep = steps.shift()
  26. if (nextStep) {
  27. nextStep({ ...this.props, div })
  28. } else {
  29. unmountComponentAtNode(div)
  30. }
  31. }
  32. render() {
  33. return this.props.children
  34. }
  35. }
  36. class Test extends React.Component {
  37. render() {
  38. return (
  39. <MemoryRouter
  40. initialIndex={initialIndex}
  41. initialEntries={initialEntries}
  42. >
  43. <Route render={(props) => (
  44. <Assert {...props}>
  45. <Subject/>
  46. </Assert>
  47. )}/>
  48. </MemoryRouter>
  49. )
  50. }
  51. }
  52. render(<Test/>, div)
  53. }
  54. // 我们的主题是这个App,但是你也可以测试你的应用的一个小部分
  55. const App = () => (
  56. <div>
  57. <Route exact path="/" render={() => (
  58. <div>
  59. <h1>Welcome</h1>
  60. </div>
  61. )}/>
  62. <Route path="/dashboard" render={() => (
  63. <div>
  64. <h1>Dashboard</h1>
  65. <Link to="/" id="click-me">Home</Link>
  66. </div>
  67. )}/>
  68. </div>
  69. )
  70. // 实际测试!
  71. it('navigates around', (done) => {
  72. renderTestSequence({
  73. // 告诉它你正在测试的 subject
  74. subject: App,
  75. // 以及每次地址(location)改变时执行的步骤
  76. steps: [
  77. // 初始渲染
  78. ({ history, div }) => {
  79. // assert the screen says what we think it should
  80. //断言屏幕的输出和我们期望的输出是否一样
  81. console.assert(div.innerHTML.match(/Welcome/))
  82. // 现在我们可以强制导航作为测试
  83. history.push('/dashboard')
  84. },
  85. // 从新的地址(location)发起二次渲染
  86. ({ div }) => {
  87. console.assert(div.innerHTML.match(/Dashboard/))
  88. //或者我们可以模拟点击链接,而不使用 history.push
  89. Simulate.click(div.querySelector('#click-me'), {
  90. button: 0
  91. })
  92. },
  93. // 最终渲染
  94. ({ location }) => {
  95. console.assert(location.pathname === '/')
  96. //你需要在这里写下 `done()`,如果你从未这样做,你的测试是失败的。
  97. done()
  98. }
  99. ]
  100. })
  101. })