我有一个包含扩展RouteComponentProps
的道具的组件,如下所示:
export interface RouteComponentProps<P> {
match: match<P>;
location: H.Location;
history: H.History;
staticContext?: any;
}
现在,当我在应用程序中使用我的组件时,我将这些道具传递给它:
<MyComponent
match={this.props.match}
location={this.props.location}
history={this.props.history}
/>
道具已经可用,因为它在反应路由器内部运行。
现在,如何在没有match
,location
,history
的情况下测试此组件?
我是否需要模拟它们,或者是否应该以某种方式自动加载一些辅助函数?
答案 0 :(得分:6)
如果您使用的是Jest(或其他)模拟库,则可以提供一个简单的模拟,并且测试将通过-以下示例使用了jest:
test('renders without crashing', () => {
let mock: any = jest.fn();
const wrapper = enzyme.mount(
<TeamSetupApp
match = {mock}
location = {mock}
history= {mock} />
);
expect(wrapper).not.toBeNull();
});
很显然,如果您想在测试中使用匹配,位置或历史记录的元素来使代码遵循特定的路径,则匹配/位置/历史记录应根据需要分别进行模拟。
答案 1 :(得分:3)
要回答您的最后一个问题,建议的方法是在测试中使用<MemoryRouter>< *your component here* ></MemoryRouter>
。 Typescript不会接受此组件会将所需的属性传递给您的组件,因此我认为不是一种类型安全的方法。
这适用于React Router v4,不适用于以前的版本。
对于一种类型安全的方法来测试用HOC withRouter
包装的组件,您可以从react-router
和history
包中构建位置,历史记录和匹配项。
此示例使用酶和快照测试,但可以像其他任何测试一样容易。
这避免了我需要使用<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();
});
注意请勿使用它来测试实现细节,这可能很诱人,但是如果您要重构,它会给您带来很多痛苦。
为此提供帮助可能是使它可重复使用的最佳方法。
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;
};
现在我们可以在测试中使用routerTestProps
函数
const { history, location, match } = routerTestProps('/route/:id', { id: '1' });
答案 2 :(得分:3)
一位名叫蒂米·黄(Timmy Huang)的绅士提供了一个解决方案,其中涉及一个简单的模拟...
const routeComponentPropsMock = {
history: {} as any,
location: {} as any,
match: {} as any,
}
我使用Jest进行了尝试,并且成功了。我的组件具有此签名...
export const MyComponent: React.FC<RouteComponentProps> = ({location}:RouteComponentProps) => {
我确认组件负载的基本测试如下:
function renderMyComponent() {
return render(
<MyComponent {...routeComponentPropsMock}/>
);
}
答案 3 :(得分:2)
我一直在寻找一个很好的解决方案。我希望我可以在mapStateToProps函数或类似的东西中做到这一点,但还没有能够做到这一点。
我能做的最好的就是嘲笑这个并传递比赛,位置和历史。我使用了以下内容:
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;
}
然后在我的测试中我做了:
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()}
/>
);