Reactjs 如何模拟路由器上下文
我有一个相当简单的react组件(链接包装器,如果路由是活动的,它会添加'active'类):Reactjs 如何模拟路由器上下文,reactjs,react-router,enzyme,Reactjs,React Router,Enzyme,我有一个相当简单的react组件(链接包装器,如果路由是活动的,它会添加'active'类): import React,{PropTypes}来自'React'; 从“反应路由器”导入{Link}; 常量导航链接=(道具、上下文)=>{ const isActive=context.router.isActive(props.to,true); const activeClass=isActive?'active':''; 返回( {props.children} ); } NavLink
import React,{PropTypes}来自'React';
从“反应路由器”导入{Link};
常量导航链接=(道具、上下文)=>{
const isActive=context.router.isActive(props.to,true);
const activeClass=isActive?'active':'';
返回(
{props.children}
);
}
NavLink.contextTypes={
路由器:PropTypes.object,
};
NavLink.propTypes={
子项:PropTypes.node,
至:PropTypes.string,
};
导出默认导航链接;
我应该如何测试它?我唯一的尝试是:
import NavLink from '../index';
import expect from 'expect';
import { mount } from 'enzyme';
import React from 'react';
describe('<NavLink />', () => {
it('should add active class', () => {
const renderedComponent = mount(<NavLink to="/home" />, { router: { pathname: '/home' } });
expect(renderedComponent.hasClass('active')).toEqual(true);
});
});
从“../index”导入导航链接;
从“expect”导入expect;
从“酶”导入{mount};
从“React”导入React;
描述(“”,()=>{
它('应该添加活动类',()=>{
const renderedComponent=mount(,{router:{pathname:'/home'});
expect(renderedComponent.hasClass('active')).toEqual(true);
});
});
它不工作并返回
TypeError:无法读取未定义的属性“isActive”
。它确实需要一些路由器模拟,但我不知道如何编写它。测试依赖于上下文的组件可能有点棘手。我所做的是编写一个在测试中使用的包装器
您可以在下面找到包装器:
import React, { PropTypes } from 'react'
export default class WithContext extends React.Component {
static propTypes = {
children: PropTypes.any,
context: PropTypes.object
}
validateChildren () {
if (this.props.children === undefined) {
throw new Error('No child components were passed into WithContext')
}
if (this.props.children.length > 1) {
throw new Error('You can only pass one child component into WithContext')
}
}
render () {
class WithContext extends React.Component {
getChildContext () {
return this.props.context
}
render () {
return this.props.children
}
}
const context = this.props.context
WithContext.childContextTypes = {}
for (let propertyName in context) {
WithContext.childContextTypes[propertyName] = PropTypes.any
}
this.validateChildren()
return (
<WithContext context={this.props.context}>
{this.props.children}
</WithContext>
)
}
}
import React,{PropTypes}来自“React”
导出默认类WithContext扩展React.Component{
静态类型={
孩子们:任何人,
上下文:PropTypes.object
}
验证儿童(){
if(this.props.children===未定义){
抛出新错误('未将任何子组件传递到WithContext')
}
如果(this.props.children.length>1){
抛出新错误('您只能将一个子组件传递到WithContext')
}
}
渲染(){
类WithContext扩展了React.Component{
getChildContext(){
返回this.props.context
}
渲染(){
归还这个。道具。孩子们
}
}
const context=this.props.context
WithContext.childContextTypes={}
for(让propertyName在上下文中){
WithContext.childContextTypes[propertyName]=PropTypes.any
}
this.validateChildren()
返回(
{this.props.children}
)
}
}
在这里,您可以看到一个示例用法:
<WithContext context={{ location: {pathname: '/Michael/Jackson/lives' }}}>
<MoonwalkComponent />
</WithContext>
<WithContext context={{ router: { isActive: true }}}>
<YourTestComponent />
</WithContext>
它应该会像你期望的那样工作。谢谢@Elon Szopos的回答,但我设法写了一些更简单的东西(如下所示):
从“../index”导入导航链接;
从“expect”导入expect;
从“酶”导入{shall};
从“React”导入React;
描述(“”,()=>{
它('应该添加活动类',()=>{
const context={router:{isActive:(a,b)=>true};
const renderedComponent=浅(,{context});
expect(renderedComponent.hasClass('active')).toEqual(true);
});
});
我必须将mount
更改为shallow
,以避免评估链接
,这会给我一个与react路由器连接的错误类型错误:router.createHref不是一个函数
我宁愿拥有“真实”的react路由器,而不仅仅是一个对象,但我不知道如何创建它。您可以将其用于确切的目的
“创建一个伪上下文对象,该对象复制React Router的context.Router结构。这对于使用Ezyme进行浅层单元测试非常有用。”
安装后,您将能够执行以下操作
describe('my test', () => {
it('renders', () => {
const context = createRouterContext()
const wrapper = shallow(<MyComponent />, { context })
})
})
description('我的测试',()=>{
它('呈现',()=>{
const context=createRouterContext()
常量包装器=浅(,{context})
})
})
对于react路由器v4,您可以使用
。AVA和酶的示例:
import React from 'react';
import PropTypes from 'prop-types';
import test from 'ava';
import { mount } from 'enzyme';
import sinon from 'sinon';
import { MemoryRouter as Router } from 'react-router-dom';
const mountWithRouter = node => mount(<Router>{node}</Router>);
test('submits form directly', t => {
const onSubmit = sinon.spy();
const wrapper = mountWithRouter(<LogInForm onSubmit={onSubmit} />);
const form = wrapper.find('form');
form.simulate('submit');
t.true(onSubmit.calledOnce);
});
从“React”导入React;
从“道具类型”导入道具类型;
从“ava”导入测试;
从“酶”导入{mount};
从“sinon”进口sinon;
从“react Router dom”导入{MemoryRouter as Router};
const mountWithRouter=node=>mount({node});
测试('直接提交表单',t=>{
const onSubmit=sinon.spy();
const wrapper=mountWithRouter();
const form=wrapper.find('form');
形式。模拟(“提交”);
t、 true(提交时调用);
});
在您需要更新包装组件的道具之前,此操作一直有效。调用setProps()
仅对根组件有效,现在它是MemoryRouter
我想不可能找到一种测试组件的通用方法。但是,在上面的示例中,可以内联设置属性。类似于onSubmit
。要允许在测试组件上使用setProps()
,可以将props如下传递给它:const TestContext=({children,…props})=>{React.cloneElement(children,props)}
注意“context”周围的大括号进入浅层-我最初错过了这些,我花了一段时间才弄明白为什么它不起作用!“注意:此软件包仅适用于React Router v4。”
describe('my test', () => {
it('renders', () => {
const context = createRouterContext()
const wrapper = shallow(<MyComponent />, { context })
})
})
import React from 'react';
import PropTypes from 'prop-types';
import test from 'ava';
import { mount } from 'enzyme';
import sinon from 'sinon';
import { MemoryRouter as Router } from 'react-router-dom';
const mountWithRouter = node => mount(<Router>{node}</Router>);
test('submits form directly', t => {
const onSubmit = sinon.spy();
const wrapper = mountWithRouter(<LogInForm onSubmit={onSubmit} />);
const form = wrapper.find('form');
form.simulate('submit');
t.true(onSubmit.calledOnce);
});