How to test react-router Links with Enzyme?
Asked Answered
B

5

9

I see quite a few similar questions to this, but all seem wildly outdated or incredibly convoluted.

I have a React component which contains a React Router Link component, like so:

export default class Home extends Component {                                      
  render() {                                                                       
    return (                                                                       
      <div className="Home">  
        <Link to="/mission">Begin</Link>                                           
      </div>                                              
    );                                                                             
  }                                                                                
}

Then, I am trying to test very simply that the Home component contains a Link component which has a to=/mission property, using Jest. So, as per the Testing React Router docs, I tried many variations of tests, but this code most succinctly demonstrates what I am trying to achieve (example uses jest-enzyme for brevity):

it('includes link to Mission scene', () => {                                       
  const home = shallow(<MemoryRouter><Home /></MemoryRouter>);                                                              
  expect(home).toContainReact(<Link to="/mission">Begin</Link>)                    
});

Obviously, that code has several issues, not the least of which is the error:

Warning: Failed context type: The context `router` is marked as required in `Link`, but its value is `undefined`.

But notice the key bits: I want to shallow render (if possible) the Home component in order to test that component in isolation. At the same time, I am trying to wrap that in a MemoryRouter or somehow get the context in place so that I can test the Link components.

Astute potential answerers will notice that this issue is actually going to prevent all isolated tests of that component (not just tests related to React Router), because I can't shallow render the component at all.

Britt answered 6/8, 2017 at 3:0 Comment(0)
A
4

You can use Enzyme's find method:

import { Link } from 'react-router';

it('includes link to Mission scene', () => {                                       
  const wrapper = shallow(<MemoryRouter><Home /></MemoryRouter>);
  expect(wrapper.find(Link).props().to).toBe('/mission');
 });
Albany answered 10/10, 2017 at 17:57 Comment(5)
I'll need to go back to this code to check this answer, but my gut says this won't work. I believe the error was raised during the shallow render, not during the expect.Britt
This cannot even work from a theoretical standpoint simply because shallow renders the outmost component only (<MemoryRouter> in this case) and the <Link> component is never exposed to the .find() method. Either use mount() instead of shallow or something like .dive().Mair
Im getting following error when I tried above answer: ReferenceError: MemoryRouter is not definedBarnette
what import am I missing?Barnette
import { MemoryRouter } from 'react-router-dom';Alurd
A
4

This is an info from official docs:

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>.

This link might be useful for you.

Anthill answered 7/6, 2018 at 8:14 Comment(0)
C
2

The below example uses Full Rendering API (mount(...)) of enzyme. So that we can find the a tag rendered by the Link component easily.

Use createMemoryHistory of history package to create memory history, because test code doesn't run in the real browser environment. And, install spy on history.push() method. Because when the user clicks the a tag, it will call history.push() method. We can check and assert this method is called or not.

So the complete code is:

Home.tsx:

import React, { Component } from 'react';
import { Link } from 'react-router-dom';

export default class Home extends Component {
  render() {
    return (
      <div className="Home">
        <Link to="/mission">Begin</Link>
      </div>
    );
  }
}

Home.test.tsx:

import { mount } from 'enzyme';
import React from 'react';
import { createMemoryHistory, MemoryHistory } from 'history';
import Home from './Home';
import { Router } from 'react-router-dom';

describe('45528156', () => {
  let memoryHistory: MemoryHistory;
  let pushSpy: jest.SpyInstance;
  beforeEach(() => {
    memoryHistory = createMemoryHistory();
    pushSpy = jest.spyOn(memoryHistory, 'push');
  });
  afterEach(() => {
    pushSpy.mockRestore();
  });
  test('should navigate to /mission route', () => {
    const wrapper = mount(
      <Router history={memoryHistory}>
        <Home />
      </Router>
    );
    const link = wrapper.find('a');
    link.simulate('click', { button: 0 });
    expect(pushSpy).toBeCalledWith('/mission');
  });
});

Test result:

 PASS  examples/45528156/Home.test.tsx (14.316 s)
  45528156
    ✓ should navigate to /mission route (41 ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 Home.tsx |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        15.915 s

package versions:

"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5",
"jest": "^26.6.3",
"react": "^16.14.0",
"react-router-dom": "^5.2.0",
Corrode answered 31/12, 2021 at 2:56 Comment(0)
S
1

you can query by just inserting the name of a componet as string like any regular html element

it('includes link to Mission scene', function() {
    const wrapper = shallow(<MemoryRouter><Home /></MemoryRouter>);
    expect(wrapper.find('Link').prop('to')).to.be.equal('/mission');
});
Stoneham answered 20/9, 2018 at 22:35 Comment(0)
U
-2

try <MemoryRouter><Route><Home /></Route></MemoryRouter>

Udell answered 15/8, 2018 at 4:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.