ger*_*lol 7 javascript reactjs redux enzyme react-redux
我创建了一个高阶组件/组合组件,以确保在加载组件之前对用户进行身份验证.这是非常基本的,但我在测试它时遇到了一些麻烦.我想测试下面的几点,这与我在其他地方已有的测试类似:
className)props(在我的情况下authenticated)authenticated渲染nullHOC:
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);
  });
});
这里的"棘手"部分是你的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);
    });
  });
});