Jest, Enzyme: Invariant Violation: You should not use <Route> or withRouter() outside a <Router>

ReactjsReact RouterJestjsEnzymeReact Router-Dom

Reactjs Problem Overview


I have a <UserListComponent /> which outputs one <Contact /> component and list of contacts presentated by <Contacts />.

The problem is that in the test for <UserListComponent /> when I try to mount it, test outputs an error Invariant Violation: You should not use <Route> or withRouter() outside a <Router>

withRouter() is used in <Contacts /> component.

How can I mock ContactsComponent without router in test of parent component?

I found some similar issue https://www.bountysource.com/issues/49297944-invariant-violation-you-should-not-use-route-or-withrouter-outside-a-router but it only describes situation when component is cover by withRouter() itself, not children.

UserList.test.jsx

const mockResp = {
  count: 2,
  items: [
    {
      _id: 1,
      name: 'User1',
      email: '[email protected]',
      phone: '+123456',
      online: false
    },
    {
      _id: 2,
      name: 'User2',
      email: '[email protected]',
      phone: '+789123',
      online: false
    },
    {
      _id: 3,
      name: 'User3',
      email: '[email protected]',
      phone: '+258369147',
      online: false
    }
  ],
  next: null
}

describe('UserList', () => {
  beforeEach(() => {
    fetch.resetMocks()
  });

  test('should output list of users', () => {
    fetch.mockResponseOnce(JSON.stringify(mockResp));

    const wrapper = mount(<UserListComponent user={mockResp.items[2]} />);
    
    expect(wrapper.find('.contact_small')).to.have.length(3);
  });

})

UserList.jsx

export class UserListComponent extends PureComponent {
  render() {
    const { users, error } = this.state;
    return (
      <React.Fragment>
        <Contact
          userName={this.props.user.name}
          content={this.props.user.phone}
        />
        {error ? <p>{error.message}</p> : <Contacts type="contactList" user={this.props.user} contacts={users} />}
      </React.Fragment>
    );
  }
}

Contacts.jsx

class ContactsComponent extends Component {
  constructor() {
    super();
    this.state = {
      error: null,
    };
  }

  render() {
    return (
      <React.Fragment>
        <SectionTitle title="Contacts" />
        <div className="contacts">
         //contacts
        </div>
      </React.Fragment>
    );
  }
}

export const Contacts = withRouter(ContactsComponent);

Reactjs Solutions


Solution 1 - Reactjs

To test a component (with Jest) that contains <Route> and withRouter you need to import Router in you test, not in your component

import { BrowserRouter as Router } from 'react-router-dom';

and use it like this

app = shallow(
	<Router>
		<App />
	</Router>);

Solution 2 - Reactjs

Utility Function To Wrap Mount With Context

Wrapping mount with Router in tests works, but there are situations where you don't want Router to be the parent component in your mount. Therefore I'm currently injecting context into mount using a wrapper function:

import { BrowserRouter } from 'react-router-dom';
import Enzyme, { shallow, mount } from 'enzyme';

import { shape } from 'prop-types';

// Instantiate router context
const router = {
  history: new BrowserRouter().history,
  route: {
    location: {},
    match: {},
  },
};

const createContext = () => ({
  context: { router },
  childContextTypes: { router: shape({}) },
});

export function mountWrap(node) {
  return mount(node, createContext());
}

export function shallowWrap(node) {
  return shallow(node, createContext());
}

This could be in a file called, say contextWrap.js, in a test helpers directory.

Example describe block:

import React from 'react';
import { TableC } from '../../src/tablec';
import { mountWrap, shallowWrap } from '../testhelp/contextWrap';
import { expectedProps } from './mockdata'

describe('Table', () => {
  let props;
  let component;
  const wrappedShallow = () => shallowWrap(<TableC {...props} />);

  const wrappedMount = () => mountWrap(<TableC {...props} />);

  beforeEach(() => {
    props = {
      query: {
        data: tableData,
        refetch: jest.fn(),
      },
    };
    if (component) component.unmount();
  });

  test('should render with mock data in snapshot', () => {
    const wrapper = wrappedShallow();
    expect(wrapper).toMatchSnapshot();
  });

  test('should call a DeepTable with correct props', () => {
    const wrapper = wrappedMount();
    expect(wrapper.find('DeepTable').props()).toEqual(expectedProps);
  });

});

You can also use this pattern to wrap subcomponents in other types of context, for example if you are using react-intl, material-ui or your own context types.

Solution 3 - Reactjs

You need to wrap the App with a BrowserRouter or an equivalent, see the below example of simple test case a component App that uses React Router

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import App from "./App";

it("renders without crashing", () => {
  const div = document.createElement("div");
  ReactDOM.render(
<BrowserRouter>
  <App />
</BrowserRouter>,
div
  );
  ReactDOM.unmountComponentAtNode(div);
})

Solution 4 - Reactjs

i had same problem, and first comment helped me, but there are a lot of code i have better way how to resolve this problem. See my solution below:

    import React from 'react';
import { shallow, mount } from 'enzyme';
import SisuSignIn from '../../../components/Sisu/SisuSignIn.js';
import { MemoryRouter } from 'react-router-dom';

const Container = SisuSignIn;

let wrapper;

beforeEach(() => {
  wrapper = shallow(<Container />);
});

describe('SisuSignIn', () => {
  it('renders correctly', () => {
    expect(wrapper).toMatchSnapshot();
  });
  it('should render one <h1>', () => {
    const wrapper = mount(
      <MemoryRouter>
        <SisuSignIn auth={{ loading: false }} />
      </MemoryRouter>
    );
    expect(wrapper.find('div').length).toBe(12);
  });
});

Solution 5 - Reactjs

A keeps the history of your "URL" in memory (does not read or write to the address bar). Useful in tests and non-browser environments like React Native.

I got similar error solution is wrapping component with the help of Memory router

import { MemoryRouter } from 'react-router'

<MemoryRouter>
  <App/>
</MemoryRouter>

Solution 6 - Reactjs

Add the Router tag to render it.

import * as React from "react";
import { render, mount } from 'enzyme';
import { BrowserRouter as Router } from "react-router-dom"
import CategoriesToBag from "../CategoriesToBag";



describe('Testing CategoriesToBag Component', () => {
    test("check if heading is correct", () => {
        const defaultValue = "Amazing Categories To Bag";
        const wrapper = render(<Router><CategoriesToBag /></Router>);
        expect(wrapper.find('.textBannerTitle').text()).toMatch(/Categories To Bag/);
        expect(wrapper.find('.textBannerTitle').text()).not.toMatch(/Wrong Match/);
        expect(wrapper.find('.textBannerTitle').text()).toBe(defaultValue);
    });

});

You should be good to go.

Solution 7 - Reactjs

actually i used to create snapshot tests for my components with react-test-renderer and i faced with this issue and what i did i just wrap my component with a BrowserRouter that ships with react-router-dom, you can do so like below:

import React from 'react'
import { BrowserRouter } from 'react-router-dom'
import renderer from "react-test-renderer";


import UserListComponent from './whatever'



test('some description', () => {
  
  const props = { ... }

  const jsx = (
    <BrowserRouter>
      <UserListComponent {...props} />
    </BrowserRouter>
  )

  const tree = renderer.create(jsx).toJSON()
  expect(tree).toMatchSnapshot()

})

totaly recommend useing a test factory for this kind of situations if you want to test it for multiple times!

Solution 8 - Reactjs

So I was thinking about this for ages, and I don't actually care about the implementation of the withRouter (I can assume that just works or test it elsewhere), and so I can just mock that function to return itself (much like I have done for the Link function).

jest.mock("react-router-dom", () => ({
  ...jest.requireActual("react-router-dom"),
  Link: () => <div />,
  withRouter: (x) => x,
}));

I hope this helps someone like it would have helped me several hours ago!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMaryja PiaredryjView Question on Stackoverflow
Solution 1 - ReactjsEdhar DowbakView Answer on Stackoverflow
Solution 2 - ReactjsSteve BantonView Answer on Stackoverflow
Solution 3 - ReactjsTaha AzzabiView Answer on Stackoverflow
Solution 4 - ReactjsshutsmanView Answer on Stackoverflow
Solution 5 - ReactjsAnil NamdeView Answer on Stackoverflow
Solution 6 - ReactjsSarat ChandraView Answer on Stackoverflow
Solution 7 - ReactjsamdevView Answer on Stackoverflow
Solution 8 - ReactjsRobert DesmondView Answer on Stackoverflow