【问题标题】:Enzyme testing an authentication Higher Order Component (HOC)酶测试认证高阶组件(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);
}

我正在使用 enzymejest 进行测试,但在我的测试期间没有找到成功渲染 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);
  });
});

【问题讨论】:

    标签: javascript reactjs redux react-redux enzyme


    【解决方案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);
        });
      });
    });
    

    【讨论】:

    • 感谢您的解释,您可以举例说明如何在测试中仅渲染组件吗?我使用shallowmount 尝试了很多不同的方法,但似乎无法正确渲染任何东西以进行测试。
    • 另外,您传递给mapStateToPropscomposedComponent 变量有点困惑。那仍然会正常运行吗?
    • 添加了如何测试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。很奇怪,因为实际上我在上面发布的测试都通过了。
    猜你喜欢
    • 1970-01-01
    • 2019-08-26
    • 1970-01-01
    • 2020-02-06
    • 2018-06-01
    • 1970-01-01
    • 2017-09-02
    • 2017-10-04
    • 2021-05-20
    相关资源
    最近更新 更多