source

농담, 효소:불변 위반:또는 Router()를 외부에서 사용하여서는 안 됩니다.

lovecheck 2023. 2. 9. 22:44
반응형

농담, 효소:불변 위반:또는 Router()를 외부에서 사용하여서는 안 됩니다.

나는 가지고 있다<UserListComponent />1을 출력<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: '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);

다음을 포함하는 구성 요소를 테스트하려면(Jest 사용)<Route>그리고.withRouter컴포넌트가 아닌 테스트에서 라우터를 Import해야 합니다.

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또는 이와 동등한 것을 사용하는 컴포넌트 App의 간단한 테스트 사례의 예를 참조하십시오.

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>

렌더링할 라우터 태그를 추가합니다.

import * as React from "react";
import { render, mount } from 'enzyme';
import { BrowserRouter as Router } from "react-router-dom"
import CategoriesToBag from "../CategoriesToBag";



describe('Testing CategoriesToBag Component', () => {
    test("check if heading is correct", () => {
        const defaultValue = "Amazing Categories To Bag";
        const wrapper = render(<Router><CategoriesToBag /></Router>);
        expect(wrapper.find('.textBannerTitle').text()).toMatch(/Categories To Bag/);
        expect(wrapper.find('.textBannerTitle').text()).not.toMatch(/Wrong Match/);
        expect(wrapper.find('.textBannerTitle').text()).toBe(defaultValue);
    });

});

당신은 가도 됩니다.

실제로 컴포넌트의 스냅숏 테스트를 작성했습니다.react-test-renderer그리고 이 문제에 직면했고 제가 한 일은 제 컴포넌트를BrowserRouter와 함께 출하되는react-router-dom, 다음과 같이 실행할 수 있습니다.

import React from 'react'
import { BrowserRouter } from 'react-router-dom'
import renderer from "react-test-renderer";


import UserListComponent from './whatever'



test('some description', () => {
  
  const props = { ... }

  const jsx = (
    <BrowserRouter>
      <UserListComponent {...props} />
    </BrowserRouter>
  )

  const tree = renderer.create(jsx).toJSON()
  expect(tree).toMatchSnapshot()

})

totaly는 여러 번 테스트하고 싶다면 이러한 상황에서 테스트 팩토리를 사용할 것을 권장합니다.

그래서 저는 오랫동안 이 문제에 대해 생각하고 있었습니다만, 실제로는 withRouter의 실장에 대해서는 신경쓰지 않습니다(다른 곳에서 동작하거나 테스트하는 것으로 가정할 수 있습니다).따라서 그 함수는 스스로 되돌리기 위해 조롱할 수 있습니다(Link 함수에서 했던 것과 거의 같습니다).

jest.mock("react-router-dom", () => ({
  ...jest.requireActual("react-router-dom"),
  Link: () => <div />,
  withRouter: (x) => x,
}));

몇 시간 전만 해도 도움이 됐을 것 같은 사람에게 도움이 됐으면 좋겠어요!

언급URL : https://stackoverflow.com/questions/50025717/jest-enzyme-invariant-violation-you-should-not-use-route-or-withrouter-ou

반응형