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