1つの<UserListComponent />
コンポーネントと<Contact />
によって提示された連絡先のリストを出力する<Contacts />
があります。
問題は、<UserListComponent />
のテストでマウントしようとすると、テストがエラーInvariant Violation: You should not use <Route> or withRouter() outside a <Router>
を出力することです。
withRouter()
は<Contacts />
コンポーネントで使用されます。
親コンポーネントのテストでルーターなしでContactsComponent
をモックするにはどうすればよいですか?
同様の問題がいくつか見つかりました https://www.bountysource.com/issues/49297944-invariant-violation-you-should-not-use-route-or-withrouter-outside-a-router コンポーネントが子ではなくwithRouter()
自体によってカバーされる場合の状況のみを説明します。
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);
<Route>
およびwithRouter
を含むコンポーネント(Jestを含む)をテストするには、コンポーネントではなく、テストでルーターをインポートする必要があります。
import { BrowserRouter as Router } from 'react-router-dom';
そして、このように使用します
app = shallow(
<Router>
<App />
</Router>);
テストでルーターでマウントをラップすることはできますが、ルーターをマウントの親コンポーネントにしたくない場合があります。したがって、現在、ラッパー関数を使用してコンテキストをマウントに注入しています。
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());
}
これは、テストヘルパーディレクトリのcontextWrap.jsなどのファイルにあります。
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);
});
});
また、このパターンを使用して、react-intl、material-ui、または独自のコンテキストタイプを使用している場合など、他のタイプのコンテキストでサブコンポーネントをラップすることもできます。
App
をBrowserRouter
または同等のものでラップする必要があります。以下の単純なテストケースの例を参照してくださいReactルーターを使用するコンポーネントアプリ
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);
})
私は同じ問題を抱えていて、最初のコメントは助けてくれましたが、この問題を解決するより良い方法があるコードがたくさんあります。以下の私の解決策を参照してください:
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);
});
});
Aは、メモリに「URL」の履歴を保持します(アドレスバーの読み取りまたは書き込みは行いません)。テストおよびReact Nativeなどの非ブラウザー環境で役立ちます。
私は同様のエラーソリューションを得ました メモリルーター の助けを借りてコンポーネントをラップしています
import { MemoryRouter } from 'react-router'
<MemoryRouter>
<App/>
</MemoryRouter>