typescript 如何使用 RouteComponentProps 测试 React 组件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/46927392/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 04:59:30  来源:igfitidea点击:

How to test a React component with RouteComponentProps?

reactjstypescripttestingreact-routerjestjs

提问by Sergei Basharov

I have a component that has props which extend RouteComponentPropsthat looks like this:

我有一个组件,它的 props 扩展RouteComponentProps如下:

export interface RouteComponentProps<P> {
  match: match<P>;
  location: H.Location;
  history: H.History;
  staticContext?: any;
}

Now, when I use my component in the app, I pass these props to it:

现在,当我在应用程序中使用我的组件时,我将这些道具传递给它:

<MyComponent
    match={this.props.match}
    location={this.props.location}
    history={this.props.history}
/>

The props are available already because it's running inside react router.

props 已经可用,因为它在 react 路由器中运行。

Now, how do I test this component without match, location, historyavailable?

现在,如何在没有match, location,history可用的情况下测试此组件?

Do I need to mock them or is it supposed to somehow automatically load them with some helper function?

我需要模拟它们还是应该以某种方式使用一些辅助函数自动加载它们?

回答by David Barker

To answer your last question, the recommended approach is to use <MemoryRouter>< *your component here* ></MemoryRouter>in your tests. Typescript does not pick up that this component will pass the required props to your component, as such I assume it notto be a type safe approach.

要回答您的最后一个问题,推荐的方法是<MemoryRouter>< *your component here* ></MemoryRouter>在您的测试中使用。Typescript 没有发现该组件会将所需的道具传递给您的组件,因此我认为它不是一种类型安全的方法。

This is for React Router v4 and doesn't apply to previous versions.

这适用于 React Router v4,不适用于以前的版本。

For a typesafe method to test components that are wrapped with the HOC withRouteryou can build the location, history and match from the react-routerand historypackages.

对于测试用 HOC 包装的组件的类型安全方法,withRouter您可以从react-routerhistory包构建位置、历史和匹配。

This example uses enzyme and snapshot testing but could just as easily be any other test.

这个例子使用酶和快照测试,但也可以很容易地使用任何其他测试。

This avoided me needing to use <MemoryRouter>as a wrapper that typescript did not like anyhow.

这避免了我需要使用<MemoryRouter>打字稿无论如何都不喜欢的包装器。

// Other imports here
import { createMemoryHistory, createLocation } from 'history';
import { match } from 'react-router';

const history = createMemoryHistory();
const path = `/route/:id`;

const match: match<{ id: string }> = {
    isExact: false,
    path,
    url: path.replace(':id', '1'),
    params: { id: "1" }
};

const location = createLocation(match.url);

test('shallow render', () => {
    const wrapper = shallow(
        <MyComponent history={history}
                     location={location}
                     match={match} />
    );

    expect(wrapper).toMatchSnapshot();
});

CAUTIONDo not use this to test implementation detail, it can be tempting but it will cause you a lot of pain should you want to refactor.

注意不要用它来测试实现细节,它可能很诱人,但如果你想重构它会给你带来很多痛苦。

Making a helper for this would probably be the best way to make this re-usable.

为此制作一个助手可能是使其可重用的最佳方式。

import { createLocation, createMemoryHistory } from 'history';
import { match as routerMatch } from 'react-router';

type MatchParameter<Params> = { [K in keyof Params]?: string };

export const routerTestProps = <Params extends MatchParameter<Params> = {}>
    (path: string, params: Params, extendMatch: Partial<routerMatch<any>> = {}) => {
        const match: routerMatch<Params> = Object.assign({}, {
            isExact: false,
            path,
            url: generateUrl(path, params),
            params
        }, extendMatch);
        const history = createMemoryHistory();
        const location = createLocation(match.url);

        return { history, location, match };
    };


const generateUrl = <Params extends MatchParameter<Params>>
    (path: string, params: Params): string => {
        let tempPath = path;

        for (const param in params) {
            if (params.hasOwnProperty(param)) {
                const value = params[param];
                tempPath = tempPath.replace(
                    `:${param}`, value as NonNullable<typeof value>
                );
            }
        }

        return tempPath;
    };

Now we can just use the routerTestPropsfunction in our tests

现在我们可以routerTestProps在我们的测试中使用这个函数

const { history, location, match } = routerTestProps('/route/:id', { id: '1' });

回答by Aziz

I have been looking for a good solution to this. I was hoping I could do it in the mapStateToProps function or something simular, but have not been able to do this yet.

我一直在寻找一个很好的解决方案。我希望我可以在 mapStateToProps 函数或类似的东西中做到这一点,但还没有能够做到这一点。

The best I could do was mock this out and pass in the match, location and history. I used the following:

我能做的最好的事情就是对此进行模拟并传递比赛,位置和历史。我使用了以下内容:

import { RouteComponentProps } from 'react-router'
import { match } from 'react-router-dom';
import {UnregisterCallback, Href} from 'history'

export function getMockRouterProps<P>(data: P) {

    var location: {
            hash: "",
            key: "",
            pathname: "",
            search: "",
            state: {}
        };

    var props: RouteComponentProps<P> = {
    match: {
            isExact: true,
            params: data,
            path: "",
            url: ""
        },
        location: location,
        history: {
            length:2,
            action:"POP",
            location: location,
            push: () => {},
            replace: () => {},
            go: (num) => {},
            goBack: () => {},
            goForward: () => {},
            block: (t) => {
                var temp: UnregisterCallback = null;
                return temp;
            },
            createHref: (t) => {
                var temp: Href = "";
                return temp;
            },
            listen: (t) => {
                var temp: UnregisterCallback = null;
                return temp;
            }

        },
        staticContext: {
        }
    };


    return props;
}

Then in my test I did:

然后在我的测试中我做了:

    var routerProps = getMockRouterProps<ReduxTestComponentProps>(null);

    const wrapper = mount<ReduxTestComponent, ReduxTestComponentState>(
            <ReduxTestComponent
                history={routerProps.history}
                location={routerProps.location}
                match={routerProps.match}
                isLoadingTodo={false}
                todos={todos}
                addAsyncTodoActionDispatch={() => mockTodoAddDispatch()}
                deleteTodoActionDispatch={() => mockTodoDeleteDispatch()}
                />
      );

回答by Danoz

A gentleman by the name of Timmy Huang provided a solution that involves a simple mock...

一位名叫 Timmy Huang 的先生提供了一个解决方案,其中涉及一个简单的模拟......

https://spectrum.chat/react/help/how-do-you-test-components-that-use-routecomponentprops~495fe95b-6925-4e7f-bfe8-65a737c5d24e?m=MTU4Mjk1MjQ4ODQ0MA==

https://spectrum.chat/react/help/how-do-you-test-components-that-use-routecomponentprops~495fe95b-6925-4e7f-bfe8-65a737c5d24e?m=MTU4Mjk1MjQ4ODQ0MA==

const routeComponentPropsMock = {
  history: {} as any,
  location: {} as any,
  match: {} as any,
}

I tried this using Jest and it worked. My component had this signature...

我用 Jest 试过这个,它奏效了。我的组件有这个签名...

export const MyComponent: React.FC<RouteComponentProps> = ({location}:RouteComponentProps) => {

My basic test to confirm the component loads then looked like this...

我确认组件负载的基本测试看起来像这样......

function renderMyComponent() {
  return render(
    <MyComponent {...routeComponentPropsMock}/>
  );
}