88 votes

Jest, Enzyme : Violation d'invariant : Vous ne devez pas utiliser <Route> ou withRouter() en dehors d'un <Router>.

J'ai un <UserListComponent /> qui produit un <Contact /> et la liste des contacts présentée par <Contacts /> .

Le problème est que dans le test pour <UserListComponent /> quand j'essaie de le monter, test sort une erreur Invariant Violation: You should not use <Route> or withRouter() outside a <Router>

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

Comment puis-je me moquer ContactsComponent sans routeur dans le 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 ne décrit que la situation où le composant est couvert par withRouter() lui-même, pas les enfants.

UserList.test.jsx

const mockResp = {
  count: 2,
  items: [
    {
      _id: 1,
      name: 'User1',
      email: 'email1@gmail.com',
      phone: '+123456',
      online: false
    },
    {
      _id: 2,
      name: 'User2',
      email: 'email2@gmail.com',
      phone: '+789123',
      online: false
    },
    {
      _id: 3,
      name: 'User3',
      email: 'email3@gmail.com',
      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);

141voto

Edhar Dowbak Points 720

Pour tester un composant (avec Jest) qui contient <Route> y withRouter Vous devez importer Router dans votre test, et non dans votre composant.

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

et l'utiliser comme ceci

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

4 votes

Violation des invariants : Impossible de trouver "store" dans le contexte ou les props de "Connect(Header)". Il faut soit envelopper le composant Root dans un <Provider>, soit passer explicitement "store" comme une propriété à "Connect(Header)".

6 votes

@AnupamMaurya utiliser MemoryRouter au lieu de BrowserRouter et ça marchera.

0 votes

J'obtiens la même erreur que @AnupamMaurya et j'ai utilisé memory router

27voto

Steve Banton Points 408

Fonction utilitaire pour envelopper le montage avec le contexte

Envelopper mount avec Router dans les tests fonctionne, mais il y a des situations où vous ne voulez pas que Router soit le composant parent dans votre mount. C'est pourquoi j'injecte actuellement le contexte dans mount en utilisant 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());
}

Il peut s'agir d'un fichier appelé, par exemple, contextWrap.js, dans un répertoire d'aides de test.

Exemple de bloc de description :

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 envelopper des sous-composants dans d'autres types de contexte, par exemple si vous utilisez react-intl, material-ui ou vos propres types de contexte.

0 votes

D'où vient la fonction 'mount' dans le premier fichier ?

0 votes

C'est le montage d'enzyme - je crois que je le configure globalement avec un fichier jestSetup avant d'invoquer des tests qui effectuent import Enzyme, { shallow, mount } from 'enzyme'; et fixe global.mount = mount; . Ajouté à la réponse pour clarification, merci beaucoup.

2 votes

Cela devrait être la réponse acceptée. Je pense que c'est beaucoup plus propre que d'utiliser l'approche des réponses acceptées.

14voto

Taha Azzabi Points 579

Vous devez envelopper le App avec un BrowserRouter ou un équivalent, voir l'exemple ci-dessous d'un simple scénario de test d'un composant App 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);
})

0 votes

Violation des invariants : Le type d'élément n'est pas valide : on s'attendait à une chaîne (pour les composants intégrés) ou à une classe/fonction (pour les composants composites) mais on a obtenu : object.

0 votes

Violation des invariants : Impossible de trouver "store" dans le contexte ou les props de "Connect(BaseLayout)". Il faut soit envelopper le composant Root dans un <Provider>, soit passer explicitement "store" comme une propriété à "Connect(BaseLayout)".

10voto

shuts13 Points 277

J'ai eu le même problème, et le premier commentaire m'a aidé, mais il y a beaucoup de code J'ai une meilleure façon de résoudre ce problème. Voir ma solution ci-dessous :

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

0 votes

Ce commentaire devrait être plus haut car il fonctionne.

7voto

Anil Namde Points 2291

A conserve l'historique de votre "URL" en mémoire (ne lit ni n'écrit dans la barre d'adresse). Utile dans les tests et les environnements non-browser comme React Native.

J'ai obtenu une erreur similaire, la solution consiste à envelopper le composant avec l'aide de Routeur de mémoire

import { MemoryRouter } from 'react-router'

<MemoryRouter>
  <App/>
</MemoryRouter>

0 votes

Oui, pour les tests, MemoryRouter devrait être préféré à BrowserRouter.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X