酶测试认证高阶组件(HOC)
Posted
技术标签:
【中文标题】酶测试认证高阶组件(HOC)【英文标题】:Enzyme testing an authentication Higher Order Component (HOC) 【发布时间】:2017-06-03 20:31:39 【问题描述】:我创建了一个高阶组件/组合组件,以确保在加载组件之前对用户进行身份验证。这是非常基本的,但我在测试它时遇到了一些麻烦。我想测试以下几点,这与我在其他地方已经进行的测试类似:
呈现组件(我通常通过查找特定于组件的className
来检查)
有正确的props
(在我的情况下是authenticated
)
如果是authenticated
,则呈现包装的组件,如果不是,则呈现null
HOC:
import React from 'react';
import connect from 'react-redux';
import createStructuredSelector from 'reselect';
import makeSelectAuthenticated from 'containers/App/selectors';
export default function RequireAuth(ComposedComponent)
class AuthenticatedComponent extends React.Component
static contextTypes =
router: React.PropTypes.object,
static propTypes =
authenticated: React.PropTypes.bool,
componentWillMount()
if (!this.props.authenticated) this.context.router.push('/');
componentWillUpdate(nextProps)
if (!nextProps.authenticated) this.context.router.push('/');
render()
return (
<div className="authenticated">
this.props.authenticated ? <ComposedComponent ...this.props /> : null
</div>
);
const mapStateToProps = createStructuredSelector(
authenticated: makeSelectAuthenticated(),
);
return connect(mapStateToProps)(AuthenticatedComponent);
我正在使用 enzyme
和 jest
进行测试,但在我的测试期间没有找到成功渲染 HOC 的方法。
有什么想法吗?
感谢以下回答:
import React from 'react';
import shallow, mount from 'enzyme';
import Provider from 'react-redux';
import AuthenticatedComponent from '../index';
describe('AuthenticatedComponent', () =>
let MockComponent;
beforeEach(() =>
MockComponent = () => <div />;
MockComponent.displayName = 'MockComponent';
);
it('renders its children when authenticated', () =>
const wrapper = shallow(
<AuthenticatedComponent
composedComponent=MockComponent
authenticated=true
/>,
context: router: push: jest.fn()
);
expect(wrapper.find('MockComponent').length).toEqual(1);
);
it('renders null when not authenticated', () =>
const wrapper = shallow(
<AuthenticatedComponent
composedComponent=MockComponent
authenticated=false
/>,
context: router: push: jest.fn()
);
expect(wrapper.find('MockComponent').length).toEqual(0);
);
);
【问题讨论】:
【参考方案1】:这里的“棘手”部分是您的 HOC 返回一个连接组件,这使得测试变得更加困难,因为您有两个浅层渲染(连接组件和实际组件)并且您必须模拟 redux 存储。
相反,您可以预先定义 AuthenticatedComponent
并将其导出为命名导出。您可以像测试其他所有组件一样独立于connect
对其进行测试:
export class AuthenticatedComponent extends React.Component
static contextTypes =
router: React.PropTypes.object,
static propTypes =
authenticated: React.PropTypes.bool,
composedComponent: React.PropTypes.any.isRequired,
componentWillMount()
if (!this.props.authenticated) this.context.router.push('/');
componentWillUpdate(nextProps)
if (!nextProps.authenticated) this.context.router.push('/');
render()
const ComposedComponent = this.props.composedComponent;
return (
<div className="authenticated">
this.props.authenticated ? <ComposedComponent ...this.props /> : null
</div>
);
export default function RequireAuth(ComposedComponent)
const mapStateToProps = () =>
const selectIsAuthenticated = makeSelectAuthenticated();
return (state) => (
authenticated: selectIsAuthenticated(state),
composedComponent: ComposedComponent,
);
;
return connect(mapStateToProps)(AuthenticatedComponent);
示例测试:
import React from 'react';
import shallow, mount from 'enzyme';
import Provider from 'react-redux';
import configureStore from 'redux-mock-store';
import RequireAuth, AuthenticatedComponent from '../';
const Component = () => <div />;
Component.displayName = 'CustomComponent';
const mockStore = configureStore([]);
describe.only('HOC', () =>
const RequireAuthComponent = RequireAuth(Component);
const context = router: push: jest.fn() ;
const wrapper = mount(
<Provider store=mockStore()>
<RequireAuthComponent />
</Provider>,
context,
childContextTypes: router: React.PropTypes.object.isRequired ,
);
it('should return a component', () =>
expect(wrapper.find('Connect(AuthenticatedComponent)')).toHaveLength(1);
);
it('should pass correct props', () =>
expect(wrapper.find('AuthenticatedComponent').props()).toEqual(
expect.objectContaining(
authenticated: false,
composedComponent: Component,
)
);
);
);
describe('rendering', () =>
describe('is authenticated', () =>
const wrapper = shallow(
<AuthenticatedComponent
composedComponent=Component
authenticated
/>,
context: router: push: jest.fn()
);
it('should render the passed component', () =>
expect(wrapper.find('CustomComponent')).toHaveLength(1);
);
);
describe('is not authenticated', () =>
const wrapper = shallow(
<AuthenticatedComponent
composedComponent=Component
authenticated=false
/>,
context: router: push: jest.fn()
);
it('should not render the passed component', () =>
expect(wrapper.find('CustomComponent')).toHaveLength(0);
);
);
);
【讨论】:
感谢您的解释,您可以举例说明如何在测试中仅渲染组件吗?我使用shallow
和mount
尝试了很多不同的方法,但似乎无法正确渲染任何东西以进行测试。
另外,您传递给mapStateToProps
的composedComponent
变量有点困惑。那仍然会正常运行吗?
添加了如何测试AuthenticatedComponent
的示例。我同意使用 mapStateToProps 传递 composedComponent
看起来有点奇怪。由于 AuthenticatedComponent
现在是在 HOC 之外定义的,因此我们可以将 ComposedComponent
传递给它的唯一方法是通过 props。我想我们可以使用mapStateToProps
或这个。
太棒了,我会将我的最终测试文件发布到 OP 以便您检查它,如果能得到您的反馈会很好。将此标记为正确答案
我在您的回答中发现了一个问题,当我尝试加载我使用的包装组件时,我使用了export default RequireAuth(SomeComponent);
...但我收到了The prop composedComponent is marked as required in AuthenticatedComponent, but its value is undefined
。很奇怪,因为实际上我在上面发布的测试都通过了。以上是关于酶测试认证高阶组件(HOC)的主要内容,如果未能解决你的问题,请参考以下文章
高阶函数HOF和高阶组件HOC(Higher Order Func/Comp)