我正在尝试编写一个模拟表单handleSubmit
中函数调用的测试,但是,我无法显示该函数已被调用。
表格如下:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import signUp from '../../actions/users/sign_up';
import PropTypes from 'prop-types';
class Signup extends Component {
constructor (props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.showError = this.showError.bind(this);
}
handleChange(event) {
const target = event.target;
this.setState({ [ target.name ]: target.value });
}
handleSubmit(event) {
event.preventDefault();
this.props.signUp(this.state);
}
showError(type) {
if (this.state && this.state.error && this.state.error.data.errors[ type ]) {
return this.state.error.data.errors[ type ][ 0 ];
}
}
componentDidUpdate (prevProps, prevState) {
const props = this.props;
if (prevProps === props) {
return;
}
this.setState({
...props,
});
}
render () {
return (
<div className='container-fluid'>
<div className='row'>
<div className='col col-md-6 offset-md-3 col-sm-12 col-12'>
<div className='card'>
<div className='card-header'>
<h4>Sign Up</h4>
</div>
<div className='card-body'>
<form onSubmit={ this.handleSubmit } >
<div className="form-row">
<div className="form-group col-md-12">
<label htmlFor="email">Email</label>
<input
type="email"
name="email"
className={ `form-control ${ this.showError('email') ? 'is-invalid' : '' }` }
id="email"
placeholder="Email"
onChange={ this.handleChange }
/>
<div className="invalid-feedback">
{ this.showError('email') }
</div>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-12">
<label htmlFor="username">Username</label>
<input
type="text"
name="username"
className={ `form-control ${ this.showError('username') ? 'is-invalid' : '' }` }
id="username"
placeholder="Username"
onChange={ this.handleChange }
/>
<div className="invalid-feedback">
{ this.showError('username') }
</div>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-12">
<label htmlFor="password">Password</label>
<input
type="password"
name="password"
className={ `form-control ${ this.showError('password') ? 'is-invalid' : '' }` }
id="password"
placeholder="Password"
onChange={ this.handleChange }
/>
<div className="invalid-feedback">
{ this.showError('password') }
</div>
</div>
<button type="submit" className="btn btn-primary">Sign Up</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
}
function mapStateToProps (state) {
return {
email: state.UsersReducer.email,
username: state.UsersReducer.username,
password: state.UsersReducer.password,
error: state.UsersReducer.error,
}
}
function mapDispatchToProps (dispatch) {
return bindActionCreators({
signUp: signUp,
}, dispatch);
}
Signup.propTypes = {
email: PropTypes.string,
username: PropTypes.string,
password: PropTypes.string,
signUp: PropTypes.func.isRequired
}
export default connect(mapStateToProps, mapDispatchToProps)(Signup);
signUp
操作如下:
import { SIGN_UP, SHOW_USER_ERRORS } from '../types';
import axios from 'axios';
import { API_ROOT, setLocalStorageHeader } from './../../api-config';
import { push } from 'react-router-redux';
export default function signUp (params) {
return dispatch => {
axios.post(`${ API_ROOT }/auth.json`, params).then(res => {
setLocalStorageHeader(res);
dispatch(push('/profile'));
dispatch(signUpAsync(res.data));
}).catch(error => {
dispatch({ type: SHOW_USER_ERRORS, payload: { error: error.response } });
});
}
}
function signUpAsync (data) {
return {
type: SIGN_UP,
payload: data
};
}
我正在尝试模拟以下事实:将使用表单输入(表单状态(email
,username
和password
)中获得的值提交表单
我目前的测试是:
import React from 'react';
import { shallow, mount } from 'enzyme';
import configureStore from 'redux-mock-store';
import { bindActionCreators } from 'redux';
import thunk from 'redux-thunk';
import Signup from '../../../components/users/signup';
import UsersReducer from '../../../reducers/reducer_users';
describe('<Signup />', () => {
describe('render()', () => {
test('submits the form data', async () => {
const mockStore = configureStore([thunk]);
const initialState = {
UsersReducer: {
email: '',
username: '',
password: '',
},
};
const store = mockStore(initialState);
const dispatchMock = jest.spyOn(store, 'dispatch');
const signUp = jest.fn();
const wrapper = shallow(<Signup store={store} signUp={signUp} />);
const component = wrapper.dive();
component.find('#email').simulate(
'change', {
target: {
name: 'email', value: 'foo@gmail.com'
}
}
);
component.find('#email').simulate(
'change', {
target: {
name: 'username', value: 'foo'
}
}
);
component.find('#password').simulate(
'change', {
target: {
name: 'password',
value: '1234567',
}
}
)
component.find('form').simulate(
'submit', {
preventDefault() {}
}
)
expect(dispatchMock).toHaveBeenCalled();
expect(signUp).toHaveBeenCalledWith({
email: 'foo@gmail.com',
username: 'foo',
password: '12345678'
});
});
});
});
但是无论尝试如何,我都会不断收到以下错误消息。
Expected mock function to have been called with:
[{"email": "foo@gmail.com", "password": "12345678", "username": "foo"}]
But it was not called.
我认为这是由于signUp
在shallow(<Signup store={store} signUp={signUp} />)
中没有得到正确的模拟,因为当我这样做时,console.log(wrapper.props())
会得到:
{
...
signUp: [Function],
...
}
不是表明它是一个模拟函数:
{ [Function: mockConstructor]
_isMockFunction: true,
...
}
我知道测试的signUp
正在调用dispatch
动作。当我向其中添加params
时,在signUp
动作中也可以看到console.log(params)
。
任何帮助将不胜感激。
答案 0 :(得分:1)
在向视图添加redux时,在signUp
中添加mapDispatchToProps
。
使用redux-mock-store
时,您可以访问store.getActions()
所调用的所有操作,因此,在您的情况下,无需传递signUp
作为间谍,该间谍将被{{1}覆盖},看起来可能像这样:
mapDispatchToProps
答案 1 :(得分:0)
因此,经过 次反复试验后,解决方案是模拟动作调用本身,方法是添加import * as signUp from '../../../actions/users/sign_up';
并对其进行模拟const signUpActionMock = jest.spyOn(signUp, 'default');
现在测试如下:
import React from 'react';
import { shallow } from 'enzyme';
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import Signup from '../../../components/users/signup';
import UsersReducer from '../../../reducers/reducer_users';
// Turns out this import allowed the signUp action to be mocked
import * as signUp from '../../../actions/users/sign_up';
describe('<Signup />', () => {
describe('render()', () => {
test('submits the form data', () => {
const middlewares = [thunk]
// Mock the signUp action call
const signUpActionMock = jest.spyOn(signUp, 'default');
const mockStore = configureStore(middlewares);
const initialState = {
UsersReducer: {
email: '',
username: '',
password: '',
},
};
const store = mockStore(initialState);
const wrapper = shallow(<Signup store={store} />);
const component = wrapper.dive();
component.find('#email').simulate(
'change', {
target: {
name: 'email', value: 'foo@gmail.com'
}
}
);
component.find('#email').simulate(
'change', {
target: {
name: 'username', value: 'foo'
}
}
);
component.find('#password').simulate(
'change', {
target: {
name: 'password',
value: '12345678',
}
}
);
component.find('form').simulate(
'submit', {
preventDefault() {}
}
);
expect(signUpActionMock).toHaveBeenCalledWith({
email: 'foo@gmail.com',
username: 'foo',
password: '12345678'
});
});
});
});