如何用 Jest 测试一个简单的异步 React 动作?

How to test a simple async React action with Jest?

我正在尝试测试 Redux 操作,需要帮​​助来测试具有副作用的操作。

这是我的操作:

export function login(email, password) {
  return dispatch => {
    dispatch(setLoginSuccess(false));
    loginApi(email, password, error => {
      dispatch(setLoginPending(false));
      if (!error) {
        dispatch(setLoginSuccess(true));
      } else {
        dispatch(setLoginError(error));
      }
    });
  }
}

下面是用于验证用户身份的 loginApi 函数:

export function loginApi(email, password, callback) {
    if (email === 'test@test.com' && password == '123') {
      return callback(null);
    } else {
      return callback(new Error('Please provide valid email and password'));
    }
};

此外,我在使用 Enzyme 和 Jest 模拟我的组件中的表单提交时遇到了问题。

这是相同的代码:

render() {
        let {email, password, emailValid} = this.state;
        let {isLoginPending, isLoginSuccess, loginError} = this.props;     
        return (
                <div className="col-md-6 col-md-offset-3 col-sm-8 col-sm-offset-2 col-xs-10 col-xs-offset-1">
                    <h3 className="text-center">Login</h3>
                    <form className="login-form" onSubmit={this.handleSubmit.bind(this)}>
                        <div className={emailValid? "form-group has-success" : (emailValid == undefined)? "form-group": "form-group has-error"}>
                            <label>Email address</label>
                            <input type="email" name="email" className="form-control" ref="userEmail" 
                            placeholder="Enter your email" onChange={this.handleChange.bind(this)}/>
                        </div>

                        {/* Checking if email valid or not */}
                        {this.props.emailValid? "" : (this.props.emailValid == undefined)? "" : <p className="text-danger">Please provide a valid email!</p>}

                        <div className="form-group">
                            <label>Password</label>
                            <input type="password" name="password" className="form-control" ref="userPassword"
                            placeholder="Enter your password" onChange={this.handleChange.bind(this)}/>
                        </div>

                        <button type ="submit" className="btn btn-primary btn-block" disabled={!this.props.emailValid}>Get Started</button>

                        {/* Displaying error messages */}
                        { loginError && <div className="auth-error-msg"><p className="text-danger">{loginError.message}</p></div> }
                    </form>
                </div>
        );
    };

这是 handleSubmit 事件的代码:

handleSubmit(e){
    e.preventDefault();
    this.props.login(this.refs.userEmail.value, this.refs.userPassword.value);
    this.setState({
      email: '',
      password: ''
    });

}

我正在尝试以这种方式模拟提交事件:

 it('should render 1 error block on submitting invalid form', () => {
        // Render a checkbox with label in the document
        const spy = jest.fn();
        const component = shallow(<Login login={spy}/>);

        const form = component.find('form').simulate('submit');

});

但它目前抛出一个错误,因为它找不到 preventDefault。我该如何测试这个事件?

我建议您拆分测试。提交表单和测试操作是两件独立的事情。要使用 jest 测试动作,您需要将动作分派到模拟商店,并查看哪个是商店的最终状态。像这样:

describe('actions', () => {
  let store

  beforeEach(() => {
    store = mockStore({})
  })

  it('should dispatch the correct actions', () => {
    const expectedActions = [
      { type: 'action1', ...arguments },
      { type: 'action2', ...arguments }
    ]

    store.dispatch(login('user', 'password'))
    expect(store.getActions()).toEqual(expectedActions)
  })
})

您可以执行多个测试用例,使预期的操作适应您作为参数传递的内容。

要创建模拟商店,有多个包可以完成这项工作。这是一个支持 thunk 的示例:

import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'

const middlewares = [ thunk ]
const mockStore = configureMockStore(middlewares)  
export default mockStore

我个人不会花太多精力测试表单提交。最后,这是标准的 html 事情,所以我会专注于您自己构建的组件。

还有另一个提示:如果您已经解决了使用 redux 的所有问题,请不要退回到正常状态。 setState 您拥有的那个 setState 将更容易实施和测试,方法是使用普通的减速器并将其也放入您的状态。