Testing react router v4 with jest enzyme

11,252

Why are you calling console.log inside of shallow? I've never seen this before... Try to wrap your <App /> component in <Route />, and search for some tag inside it. For example. Assuming you have this simple component:

<App>
  <h1>Hi there!</h1>
</App>

Test can look like that:

describe('<App />', () => {

  it('renders a static text', () => {

    const wrapper = shallow(
      <MemoryRouter initialEntries={['/']} initialIndex={0}>
        <Route path="/" render={() => <App />} />
      </MemoryRouter>
    );
    console.log(wrapper.find(App).html());
  });
});

UPDATE It happens that react-boilerplate adds way too much wrappers and dependencies. So for successfull test we need to overcome them in different ways...

This is how I got it done(full content of my react-boilerplate/app/tests/simple.test.js):

import React from 'react';
import { mount } from 'enzyme';
import { MemoryRouter, Route, browserHistory } from 'react-router-dom';
import { IntlProvider } from 'react-intl';
import { Provider } from 'react-redux';

import configureStore from '../configureStore';
import App from '../containers/App';
import { HomePage } from '../containers/HomePage';

describe('test of initial load', () => {
  it('test', () => {
    const store = configureStore({}, browserHistory);
    const wrapper = mount(
      <MemoryRouter initialEntries={['/']} initialIndex={0}>
        <IntlProvider locale="en"> // to add Intl support
          <Provider store={store} > // to provide store for numeric connected components
            <Route path="/" render={() => <App />} />
          </Provider>
        </IntlProvider>
      </MemoryRouter>
    );
    process.nextTick(() => { // to wait for loadible component will be imported
      console.log(wrapper.find(HomePage).html());
    });
  });
});

I think that this is rather integration and not unit test... Output of console.log:

    console.log app/tests/simple.test.js:24
  <article><!-- react-empty: 38 --><div><section class="sc-bxivhb jwizic"><h2 class="H2__H2-dhGylN kpbCLA"><span>Start your next react project in seconds</span></h2><p><span>A highly scalable, offline-first foundation with the best DX and a focus on performance and best practices</span></p></section><section class="Section__Section-jEbZoY eivjmS"><h2 class="H2__H2-dhGylN kpbCLA"><span>Try me!</span></h2><form class="Form__Form-PASBS cEpjmP"><label for="username"><span>Show Github repositories by</span><span class="AtPrefix__AtPrefix-ivHByA dZcxpA"><span>@</span></span><input type="text" class="Input__Input-ixjKAz jCPlXI" id="username" placeholder="mxstbr" value=""></label></form><!-- react-empty: 54 --></section></div></article>
Share:
11,252
johnwick0831
Author by

johnwick0831

Updated on June 16, 2022

Comments

  • johnwick0831
    johnwick0831 over 1 year

    I'm trying to do a simple test with react router v4 and jest enzyme.

    describe('<App />', () => {
    
      it('renders a static text', () => {
    
        const wrapper = shallow(
        <MemoryRouter initialEntries={['/']} initialIndex={0}>
          <App/>
        </MemoryRouter>
    
        console.log(wrapper.html());
      );
    
      });
    });
    

    When I try to console.log(wrapper.html()), I get:

    Invariant Violation: [React Intl] Could not find required `intl` object. <IntlProvider> needs to exist in the component ancestry.
    

    I am using the react-boiler-plate project. All I want to do is test that when I give it a path of '/' then the home page renders some text.

    Any help appreciated! Thanks!

    EDIT:

    This is the test:

    it('test', () => {
        const wrapper = shallow(
          <MemoryRouter initialEntries={['/']} initialIndex={0}>
            <Route path="/" render={() => <App/>}/>
          </MemoryRouter>,
        );
    
        console.log(wrapper.find(App).html());
      });
    

    This is the App class:

    export default function App() {
      return (
        <AppWrapper>
          <Helmet
            titleTemplate="%s - React.js Boilerplate"
            defaultTitle="React.js Boilerplate"
          >
            <meta name="description" content="A React.js Boilerplate application"/>
          </Helmet>
          <Header/>
          <Switch>
            <Route exact path="/" component={HomePage}/>
            <Route path="/features" component={FeaturePage}/>
            <Route
              path="/catalog"
              render={() => <div>hi</div>}
            />
            <Route path="" component={NotFoundPage}/>
          </Switch>
          <Footer/>
        </AppWrapper>
      );
    }
    

    This is the error message when running the test:

    **Error: Method “html” is only meant to be run on a single node. 0 found instead.**
    

    I am expecting that this page would match and I would be able to console.log the output of it:

    <Route exact path="/" component={HomePage}/>
    
  • johnwick0831
    johnwick0831 over 5 years
    Apologies, that was a typo - console.log isn't supposed to be in the shallow. I've updated the question and wrapped <App> around a <Route> and I got a new error message. I'm using the react-boiler-plate (/App/tests/index.test.js)
  • Andrew Miroshnichenko
    Andrew Miroshnichenko over 5 years
    @johnwick0831 thanks for edit! Can you please try to use mount instead of shallow as rendering method? I think shallow needs to be used in very simple tests, and test that includes routing isn't simple.
  • johnwick0831
    johnwick0831 over 5 years
    Using mount introduces many new problems. To reproduce the problem, clone react-boiler-plate. And in the /app/tests/ create the test from above. You will notice if you use mount, it will provide some error message about "Could not find required intl object. <IntlProvider> needs to exist in the component ancestry." Not really sure what the problem is, I hope someone can clone the project and give this test a shot if they have extra time on their hands...
  • johnwick0831
    johnwick0831 over 5 years
    thanks for your detailed answer. I agree this seems overly complex for a unit test and has too many injected dependencies. What would your recommendation be to test the <App> class so that given certain initialEntries it routes to the correct component ?
  • Andrew Miroshnichenko
    Andrew Miroshnichenko over 5 years
    @johnwick0831 I think that by setting a task in such way you are trying to test not the <App> class, but functionality of the react router itself:) In my practice, I leave to router responsibility to route, and just test my components with different props, supplied by router(like this.props.match.params), by mocking such props.
  • johnwick0831
    johnwick0831 over 5 years
    Hi Andrew, I agree with what you say. Would you be able to provide an example of what you said for everybody to benefit from and I'll mark that as an answer. Thank you!
  • Andrew Miroshnichenko
    Andrew Miroshnichenko over 5 years
  • Leon
    Leon over 4 years
    Super late reply, but I think he wants to confirm that the router renders what is expected
  • Alan Yong
    Alan Yong about 2 years
    none of your code testing react-router