如何模拟反应路由器上下文
How to mock react-router context
我有相当简单的反应组件(Link 包装器,如果路由处于活动状态,它会添加 'active' class):
import React, { PropTypes } from 'react';
import { Link } from 'react-router';
const NavLink = (props, context) => {
const isActive = context.router.isActive(props.to, true);
const activeClass = isActive ? 'active' : '';
return (
<li className={activeClass}>
<Link {...props}>{props.children}</Link>
</li>
);
}
NavLink.contextTypes = {
router: PropTypes.object,
};
NavLink.propTypes = {
children: PropTypes.node,
to: PropTypes.string,
};
export default NavLink;
我应该如何测试它?我唯一的尝试是:
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);
});
});
它不起作用并且 returns TypeError: Cannot read property 'isActive' of undefined
。它肯定需要一些路由器模拟,但我不知道如何编写它。
测试依赖上下文的组件可能有点棘手。我所做的是编写一个我在测试中使用的包装器。
您可以在下面找到包装器:
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>
)
}
}
在这里你可以看到一个示例用法:
<WithContext context={{ location: {pathname: '/Michael/Jackson/lives' }}}>
<MoonwalkComponent />
</WithContext>
<WithContext context={{ router: { isActive: true }}}>
<YourTestComponent />
</WithContext>
它应该会像您预期的那样工作。
感谢@Elon Szopos 的回答,但我设法写了一些更简单的东西(在 https://github.com/airbnb/enzyme/pull/62 之后):
import NavLink from '../index';
import expect from 'expect';
import { shallow } from 'enzyme';
import React from 'react';
describe('<NavLink />', () => {
it('should add active class', () => {
const context = { router: { isActive: (a, b) => true } };
const renderedComponent = shallow(<NavLink to="/home" />, { context });
expect(renderedComponent.hasClass('active')).toEqual(true);
});
});
我必须将 mount
更改为 shallow
以便不评估 Link
这给我一个与反应路由器相关的错误 TypeError: router.createHref is not a function
.
我宁愿 "real" react-router 而不是一个对象,但我不知道如何创建它。
您可以使用 https://github.com/pshrmn/react-router-test-context 来达到这个目的
"Create a pseudo context object that duplicates React Router's context.router structure. This is useful for shallow unit testing with Enzyme."
安装后,您将可以执行类似
的操作
describe('my test', () => {
it('renders', () => {
const context = createRouterContext()
const wrapper = shallow(<MyComponent />, { context })
})
})
对于 React Router v4,您可以使用 <MemoryRouter>
。 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);
});
我有相当简单的反应组件(Link 包装器,如果路由处于活动状态,它会添加 'active' class):
import React, { PropTypes } from 'react';
import { Link } from 'react-router';
const NavLink = (props, context) => {
const isActive = context.router.isActive(props.to, true);
const activeClass = isActive ? 'active' : '';
return (
<li className={activeClass}>
<Link {...props}>{props.children}</Link>
</li>
);
}
NavLink.contextTypes = {
router: PropTypes.object,
};
NavLink.propTypes = {
children: PropTypes.node,
to: PropTypes.string,
};
export default NavLink;
我应该如何测试它?我唯一的尝试是:
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);
});
});
它不起作用并且 returns TypeError: Cannot read property 'isActive' of undefined
。它肯定需要一些路由器模拟,但我不知道如何编写它。
测试依赖上下文的组件可能有点棘手。我所做的是编写一个我在测试中使用的包装器。
您可以在下面找到包装器:
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>
)
}
}
在这里你可以看到一个示例用法:
<WithContext context={{ location: {pathname: '/Michael/Jackson/lives' }}}>
<MoonwalkComponent />
</WithContext>
<WithContext context={{ router: { isActive: true }}}>
<YourTestComponent />
</WithContext>
它应该会像您预期的那样工作。
感谢@Elon Szopos 的回答,但我设法写了一些更简单的东西(在 https://github.com/airbnb/enzyme/pull/62 之后):
import NavLink from '../index';
import expect from 'expect';
import { shallow } from 'enzyme';
import React from 'react';
describe('<NavLink />', () => {
it('should add active class', () => {
const context = { router: { isActive: (a, b) => true } };
const renderedComponent = shallow(<NavLink to="/home" />, { context });
expect(renderedComponent.hasClass('active')).toEqual(true);
});
});
我必须将 mount
更改为 shallow
以便不评估 Link
这给我一个与反应路由器相关的错误 TypeError: router.createHref is not a function
.
我宁愿 "real" react-router 而不是一个对象,但我不知道如何创建它。
您可以使用 https://github.com/pshrmn/react-router-test-context 来达到这个目的
"Create a pseudo context object that duplicates React Router's context.router structure. This is useful for shallow unit testing with Enzyme."
安装后,您将可以执行类似
的操作describe('my test', () => {
it('renders', () => {
const context = createRouterContext()
const wrapper = shallow(<MyComponent />, { context })
})
})
对于 React Router v4,您可以使用 <MemoryRouter>
。 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);
});