@@ -4,10 +4,10 @@ title: React Router
44--- 
55
66``` jsx 
7+ //  app.js
78import  React  from  ' react' 
89import  { withRouter  } from  ' react-router' 
910import  { Link , Route , Router , Switch  } from  ' react-router-dom' 
10- import  { createMemoryHistory  } from  ' history' 
1111import  { render , fireEvent  } from  ' @testing-library/react' 
1212
1313const  About  =  () =>  < div> You are on the about page< / div> 
@@ -32,42 +32,96 @@ function App() {
3232 < / div> 
3333 )
3434}
35+ ``` 
3536
36- //  Ok, so here's what your tests might look like
37+ ``` jsx 
38+ //  app.test.js
39+ import  { Router  } from  ' react-router-dom' 
40+ import  { createMemoryHistory  } from  ' history' 
3741
38- //  this is a handy function that I would utilize for any component
39- //  that relies on the router being in context
42+ test (' full app rendering/navigating' =>  {
43+  const  history  =  createMemoryHistory ()
44+  const  { container , getByText  } =  render (
45+  < Router history= {history}> 
46+  < App / > 
47+  < / Router> 
48+  )
49+  //  verify page content for expected route
50+  //  often you'd use a data-testid or role query, but this is also possible
51+  expect (container .innerHTML ).toMatch (' You are home' 
52+ 
53+  fireEvent .click (getByText (/ about/ i
54+ 
55+  //  check that the content changed to the new page
56+  expect (container .innerHTML ).toMatch (' You are on the about page' 
57+ })
58+ 
59+ test (' landing on a bad page shows 404 page' =>  {
60+  const  history  =  createMemoryHistory ()
61+  history .push (' /some/bad/route' 
62+  const  { getByRole  } =  render (
63+  < Router> 
64+  < App / > 
65+  < / Router> 
66+  )
67+  expect (getByRole (' heading' toHaveTextContent (' 404 Not Found' 
68+ })
69+ 
70+ test (' rendering a component that uses withRouter' =>  {
71+  const  route  =  ' /some-route' 
72+  window .history .pushState ({}, ' ' 
73+  const  { getByTestId  } =  render (
74+  < Router> 
75+  < LocationDisplay / > 
76+  < / Router> 
77+  )
78+  expect (getByTestId (' location-display' textContent ).toBe (route)
79+ })
80+ ``` 
81+ 
82+ ## Reducing boilerplate  
83+ 
84+ 1 .  You can use the ` wrapper `  option to wrap a ` MemoryRouter `  around the component you want to render (` MemoryRouter `  works when you don't need access to the history object itself in the test, but just need the components to be able to render and navigate).
85+ 
86+ ``` jsx 
87+ import  { MemoryRouter  } from  ' react-router-dom' 
88+ 
89+ test (' full app rendering/navigating' =>  {
90+  const  { container , getByText  } =  render (< App / > , {wrapper:  MemoryRouter})
91+  //  verify page content for expected route
92+  expect (getByRole (' heading' toMatch (' Home' 
93+ })
94+ ``` 
95+ 
96+ 2 .  If you find yourself adding Router components to your tests a lot, you may want to create
97+ a helper function that wraps around ` render ` . 
98+ 
99+ ``` jsx 
100+ //  test utils file
40101function  renderWithRouter (
41102 ui ,
42103 {
43104 route =  ' /' 
44105 history =  createMemoryHistory ({ initialEntries:  [route] }),
45106 } =  {}
46107) {
108+  const  Wrapper  =  ({children}) =>  < Router history= {history}> {children}< / Router> 
47109 return  {
48-  ... render (< Router history = {history} > {ui} < / Router > ),
110+  ... render (ui, {wrapper :  Wrapper} ),
49111 //  adding `history` to the returned utilities to allow us
50112 //  to reference it in our tests (just try to avoid using
51113 //  this to test implementation details).
52114 history,
53115 }
54116}
117+ ``` 
55118
56- test (' full app rendering/navigating' =>  {
57-  const  { container , getByText  } =  renderWithRouter (< App / > )
58-  //  normally I'd use a data-testid, but just wanted to show this is also possible
59-  expect (container .innerHTML ).toMatch (' You are home' 
60-  const  leftClick  =  { button:  0  }
61-  fireEvent .click (getByText (/ about/ i
62-  //  normally I'd use a data-testid, but just wanted to show this is also possible
63-  expect (container .innerHTML ).toMatch (' You are on the about page' 
64- })
65- 
119+ ``` jsx 
120+ //  app.test.js
66121test (' landing on a bad page' =>  {
67122 const  { container  } =  renderWithRouter (< App / > , {
68123 route:  ' /something-that-does-not-match' 
69124 })
70-  //  normally I'd use a data-testid, but just wanted to show this is also possible
71125 expect (container .innerHTML ).toMatch (' No match' 
72126})
73127
@@ -77,3 +131,4 @@ test('rendering a component that uses withRouter', () => {
77131 expect (getByTestId (' location-display' textContent ).toBe (route)
78132})
79133``` 
134+ 
0 commit comments