【问题标题】:Cannot read property 'contextTypes' of undefined Unit Testing enzyme无法读取未定义单元测试酶的属性“contextTypes”
【发布时间】:2017-06-16 12:38:06
【问题描述】:

我正在使用 redux 应用程序在我的反应中尝试单元测试。所以我需要测试连接的组件不幸的是我收到了这个错误:

无法读取未定义的属性“contextTypes” 我在单元测试中使用酶这是我的组件:

    import React from 'react';
    import TextFieldGroup from '../common/TextFieldGroup';
    import validateInput from '../../server/validations/login';
    import { connect } from 'react-redux';
    import { login } from '../../actions/authActions';

    class LoginForm extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          username: '',
          password: '',
          errors: {},
          isLoading: false
        };

        this.onSubmit = this.onSubmit.bind(this);
        this.onChange = this.onChange.bind(this);
      }

      isValid() {
        const { errors, isValid } = validateInput(this.state);

        if (!isValid) {
          this.setState({ errors });
        }

        return isValid;
      }

      onSubmit(e) {
        e.preventDefault();
        if (this.isValid()) {
          this.setState({ errors: {}, isLoading: true });
          this.props.login(this.state).then(
            (res) => this.context.router.push('/'),
            (err) => this.setState({ errors: err.response.data.errors, isLoading: false })
          );
        }
      }

      onChange(e) {
        this.setState({ [e.target.name]: e.target.value });
      }

      render() {
        const { errors, username, password, isLoading } = this.state;

        return (
          <form onSubmit={this.onSubmit}>
            <h1>Login</h1>

            { errors.form && <div className="alert alert-danger">{errors.form}</div> }

            <TextFieldGroup
              field="username"
              label="Username"
              value={username}
              error={errors.username}
              onChange={this.onChange}
            />

            <TextFieldGroup
              field="password"
              label="Password"
              value={password}
              error={errors.password}
              onChange={this.onChange}
              type="password"
            />

            <div className="form-group"><button className="btn btn-primary" disabled={isLoading}>Login</button></div>
          </form>
        );
      }
    }

    LoginForm.propTypes = {
      login: React.PropTypes.func.isRequired
    }

    LoginForm.contextTypes = {
      router: React.PropTypes.object.isRequired
    }

    export default connect(null, { login })(LoginForm);

这是我的测试:

    import React from 'react';
    import { mount, shallow } from 'enzyme';
    import {expect} from 'chai';
    import sinon from 'sinon';
    import { connect } from 'react-redux'

    import { Login } from '../../js/react/components/login/LoginForm';

    describe('<Login />', function () {
      it('should have an input for the username', function () {
        const wrapper = shallow(<Login />);
        expect(wrapper.find('input[name=username]')).to.have.length(1);
      });

      it('should have an input for the password', function () {
        const wrapper = shallow(<Login />);
        expect(wrapper.find('input[name=password]')).to.have.length(1);
      });

      it('should have a button', function () {
        const wrapper = shallow(<Login />);
        expect(wrapper.find('button')).to.have.length(1);
      });

      it('simulates click events', () => {
        const onButtonClick = sinon.spy();
        const wrapper = shallow(
          <Login onButtonClick={onButtonClick} />
        );
        wrapper.find('button').simulate('click');
        expect(onButtonClick).to.have.property('callCount', 1);
      });

    });

非常感谢您的建议和答案:)

【问题讨论】:

  • 已更改 >import { Login } from '../../js/react/components/login/LoginForm'; >从'../../js/react/components/login/LoginForm'导入登录;得到另一个错误>不变违规:在“Connect(LoginForm)”的上下文或道具中找不到“商店”。要么将根组件包装在 中,要么将“store”作为道具显式传递给“Connect(LoginForm)”

标签: unit-testing reactjs redux react-redux enzyme


【解决方案1】:

您没有导出登录组件。含义:

class LoginForm extends React.Component { ... -> export class LoginForm extends React.Component { ...

【讨论】:

    【解决方案2】:

    测试装饰组件

    要直接测试组件,您只需导出组件本身的函数而不将其传递给 Connect。

    目前,在您的测试中,您正在导入由 connect() 返回的包装器组件,而不是 LoginForm 组件本身。如果您想测试 LoginForm 组件与 Redux 的交互,这很好。如果您没有单独测试组件,那么这种导出和导入组件的方法就可以了。但是,您需要记住使用专门为此单元测试创​​建的组件来包装测试中的组件。现在让我们看看这个我们正在测试一个连接组件的案例,并解释为什么我们将它包装在我们的单元测试中。

    react-redux中Provider和Connect组件的关系

    react-redux 库为我们提供了一个 Provider 组件。 Provider 的目的是允许其任何子组件在包装在 Connect 组件中时访问 Redux 存储。 Provider 和 Connect 之间的这种共生关系允许任何封装在 Connect 组件中的组件通过 React 的 context 功能访问 Redux 存储。

    测试连接组件

    还记得连接组件是包装在 Connect 组件中的组件,并且这种包装使我们的组件可以访问 Redux 存储吗?出于这个原因,我们需要在我们的测试文件中创建一个模拟存储,因为我们需要一个存储来测试组件如何与其交互。

    在测试中为我们的 Connected 组件提供一个带有 Provider 的存储

    但是,Connect 不知道如何神奇地访问商店。它需要嵌套(包装)在组件中。 Provider 组件通过 React 的 context api 挂钩到 Provider,从而使我们包装在 Connect 中的组件能够访问 Store。

    正如我们所见,Provider 接受了一个由 Redux 存储组成的道具:

    ReactDOM.render(
          <Provider store={store}>
            <MyRootComponent />
          </Provider>,
          rootEl
        )
    

    所以要测试连接的组件,您需要用 Provider 包装它。请记住 Provider 将 Redux 存储对象作为道具。对于我们的测试,我们可以使用 redux-mock-store 库,它可以帮助我们建立一个 mock store,并在 store 上为我们提供一些方法来跟踪哪些 action 已被调度(如果我们需要它们)。

    import { Provider } from 'react-redux'
    import { mount } from 'enzyme'
    import chai, {expect} from 'chai'
    import chaiEnzyme from 'chai-enzyme'
    import configureMockStore from 'redux-mock-store'
    
    chai.use(chaiEnzyme())
    
    import ConnectedLoginForm, from '../app.js'
    const mockStore = configureMockStore(middlewares)
    
    describe.only('<LoginForm Component />', () => {
    it('LoginForm should pass a given prop to its child component' () => {
        const store = mockStore(initialState)
        const wrapper = mount(
          <Provider store={store}>
            <ConnectedLoginForm />
          </Provider>
        )
        expect(wrapper.type()).to.equal('div')
      })
    })
    

    但有时您只想测试组件的渲染,而不需要 Redux 存储。让我们看看这个案例,我们要单独测试未连接的 LoginForm 组件的渲染。

    测试未连接的组件

    因此,目前您正在测试通过使用 Connect 包装原始 LoginForm 组件而创建的新组件。

    为了在不连接的情况下测试原始组件本身,为 LoginForm 组件创建一个 单独的第二个导出声明

    import { connect } from 'react-redux'
    
    // Use named export for unconnected component (for tests)
    export class App extends Component { /* ... */ }
    
    // Use default export for the connected component (for app)
    export default connect(mapStateToProps)(App)
    

    您现在可以在没有 Redux 存储的情况下测试组件的渲染。

    在测试中导入组件

    记住当你以这种方式导出组件时-

    • 默认导出用于导入连接组件
    • 为 导入未连接的组件

    现在在您的测试文件中导入未修饰的 LoginForm 组件,如下所示:

    // Note the curly braces: grab the named export instead of default export
    import { LoginForm } from './App'
    

    或者导入未装饰和装饰(连接)的组件:

    import ConnectedLoginForm, { LoginForm } from './App'
    

    如果你想测试 LoginForm 组件如何与你的 Redux 存储交互,你必须

    【讨论】:

      猜你喜欢
      • 2017-02-09
      • 1970-01-01
      • 2020-07-17
      • 2016-11-30
      • 1970-01-01
      • 1970-01-01
      • 2017-12-14
      • 2019-12-30
      • 2021-12-02
      相关资源
      最近更新 更多