React/Redux 测试 - 在上下文或道具中找不到 "store"

React/Redux Testing - Could not find "store" in either the context or props

我是 react/redux 的新手,刚刚开始使用 chai 测试我的第一个应用程序。我正在使用 redux-form/immutable 和 react-router,我不确定在测试时如何解决这个问题:

  1) Login
       renders a login form:
     Invariant Violation: Could not find "store" in either the context or props of "Connect(Form(LoginForm))". Either wrap the root component in a <Provider>, or explicitly pass "st
ore" as a prop to "Connect(Form(LoginForm))".

我发现了这个问题:https://github.com/reactjs/react-redux/issues/57 这似乎是同一个问题,但是将 returns 子元素添加到 Router 元素的函数的解决方案并没有解决问题。

index.jsx

import {configRoutes} from './config'

ReactDOM.render(
  <Provider store={store}>
    <Router history={hashHistory}>{configRoutes()}</Router>
  </Provider>,
  document.getElementById('app')
);

config.js

import App from './App';
import {PackageStoreContainer} from './components/Package/PackageContainer';
import {LoginStoreContainer} from './components/Login/LoginContainer';

export function configRoutes() {
  const routes = <Route component={App}>
    <Route path="/packages" component={PackageStoreContainer} />
    <Route path="/" component={LoginStoreContainer} />
  </Route>;
  return routes;
}

LoginContainer.jsx

export class LoginContainer extends React.Component {
  constructor(props, context) {
    super(props, context);
    sessionStorage.removeItem('credentials');
  }
  submit = values => {
    this.props.dispatch(loginUser(values));
  }
  render() {
    return (
      <div className="row">
        <LoginForm onSubmit={this.submit} />
      </div>
    );
  }
};

const mapStateToProps = (state, ownProps) => {
  return {
    creds: {}
  };
}

export const LoginStoreContainer = connect(mapStateToProps)(LoginContainer);

LoginForm.jsx

const required = value => (value ? undefined : 'Required');
const renderField = ({input, label, type, meta: {touched, error, warning}}) => (
  <div>
    <ControlLabel htmlFor="email">{label}</ControlLabel>
    <div>
      <input {...input} placeholder={label} type={type} className="form-control" />
      {touched &&
        ((error && <span>{error}</span>) ||
          (warning && <span>{warning}</span>))}
    </div>
  </div>
);

const LoginForm = (props) => {
    const { handleSubmit } = props
    return (
      <Col xs={12} md={12}>
        <form onSubmit={handleSubmit}>
          <FormGroup>
            <Field
              name="username"
              type="email"
              component={renderField}
              label="Username"
              validate={[required]}
            />
          </FormGroup>
          <FormGroup>
            <Field
              name="password"
              type="password"
              component={renderField}
              label="Password"
              validate={[required]} />
          </FormGroup>
          <button type="submit" className="btn btn-primary">Submit</button>
        </form>
      </Col>
    );

};

export default reduxForm({
  // a unique name for the form
  form: 'login'
})(LoginForm)

Login_spec.js

describe('Login', () => {
  it('renders a login form', () => {
    const component = renderIntoDocument(
      <LoginContainer />
    );
    const fields = scryRenderedDOMComponentsWithTag(component, 'input');
    const submit = scryRenderedDOMComponentsWithTag(component, 'button');

    // expect(fields.length).to.equal(3);
    // expect(submit.length).to.equal(1);
  });
});

修复

import {Provider} from 'react-redux';

const createMockStore = (getState) => {
    const middlewares = [];
    return configureStore(middlewares)(getState);
};

const store = createMockStore();

const component = renderIntoDocument(
   <Provider store={store}>
      <LoginContainer />
   </Provider>
);

LoginContainer 是一个连接组件connect(mapStateToProps)(LoginContainer)。这意味着它依赖于 Redux 存储来生成子树,并且在测试时你没有围绕它的包装 <Provider store={store} />

解决方法是使用redux-mock-store:

import configureStore from 'redux-mock-store';
const mockStore = configureStore();

const component = renderer.create(
   <Provider store={mockStore()}>
      <LoginContainer />
   </Provider>
);

当您连接组件时,connect 函数会尝试在父层次结构中查找具有存储的提供程序。如果找不到它,则会抛出该错误。

  • 简单的解决方案是用 Provider 包装每个测试组件。

      import reducers from "reducerPath"
      import {Provider} from "react-redux"
      import {createStore} from "redux"
    
      <Provider store={createStore(reducers,{})} >
          <LoginComponent/>
      </Provider>
    

这将解决问题,但它很昂贵,因为您必须为每个组件设置它

  • 简单的方法是更改​​呈现应用程序的方式。而不是 index.jsx 中的这个:

    ReactDOM.render(
      <Provider store={store}>
        <Router history={hashHistory}>{configRoutes()}</Router>
      </Provider>,
       document.getElementById('app')
    );
    

首先创建一个包装器组件:

    import reducers from "reducerPath"
    import {Provider} from "react-redux"
    import {createStore} from "redux"


   export default (props)=>{
      <Provider store={createStore(reducers,{})}>
          {props.children}       
      </Provider>
    }

然后index.jsx

  import Wrapper from "./wrapper.jsx"

  ReactDOM.render(
    <Wrapper>
      <Router history={hashHistory}>{configRoutes()}</Router>
    </Wrapper>,
     document.getElementById('app')
  );

现在在测试中,您可以使用这个 Wrapper 组件来包装测试组件,而不是依赖于另一个第三方库:

const component = renderIntoDocument(
   <Wrapper store={store}>
      <LoginContainer />
   </Wrapper>
);