web-dev-qa-db-fra.com

Jest, Enzyme: violation invariante: vous ne devez pas utiliser <Route> ou withRouter () en dehors d'un <routeur>

J'ai un <UserListComponent /> qui génère un composant <Contact /> et la liste des contacts présentés par <Contacts />

Le problème est que dans le test de <UserListComponent />, lorsque je tente de le monter, le test génère une erreur Invariant Violation: You should not use <Route> or withRouter() outside a <Router>.

withRouter() est utilisé dans le composant <Contacts />.

Comment puis-je simuler ContactsComponent sans routeur lors du test du composant parent?

J'ai trouvé un problème similaire https://www.bountysource.com/issues/49297944-invariant-violation-you-should-not-use-route-or-withrouter-outside-a-router .__, mais il décrit uniquement la situation dans laquelle le composant est couvert par withRouter() lui-même, pas par des enfants.

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);
17
Maryja Piaredryj

Pour tester un composant (avec Jest) contenant <Route> et withRouter, vous devez importer le routeur dans votre test, pas dans votre composant.

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

et l'utiliser comme ça

app = shallow(
    <Router>
        <App />
    </Router>);
30
Edhar Dowbak

Fonction utilitaire pour envelopper le montage avec le contexte

Le montage du montage avec le routeur dans les tests fonctionne, mais il existe des situations dans lesquelles vous ne voulez pas que le routeur soit le composant parent de votre montage. Par conséquent, j'injecte actuellement du contexte dans mount à l'aide d'une fonction wrapper:

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());
}

Cela pourrait se trouver dans un fichier appelé, par exemple contextWrap.js, dans un répertoire des assistants de test. 

Exemple décrire un bloc:

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);
  });

});

Vous pouvez également utiliser ce modèle pour intégrer des sous-composants dans d'autres types de contexte, par exemple si vous utilisez les types de réaction-intl, material-ui ou vos propres contextes.

10
Steve Banton

Vous devez encapsuler la App avec une BrowserRouter ou un équivalent... Voir l'exemple ci-dessous de test élémentaire simple avec un composant qui utilise 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);
})
1
Taha Azzabi