I recently started testing my React app. However, I stumbled when dealing with submitting forms. My test covers most of the lines but misses out on actual part of submit form method.
LoginForm.js - submit form
const userLoginData = {
userId : this.state.userId,
password : this.state.password,
userType : this.state.userType
};
axios({
data : JSON.stringify(userLoginData),
type : 'post',
url : Constant.BASE_URL_SERVER+'/rest/login',
headers : {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
cache : false
})
.then(function (response) {
//alert("Form Submitted.");
this.setState({isLoggedIn : true});
this.setState({loginResponse : "Login Success!"});
if(this.state.userType === 'Customer'){
...
login_form-test.js
describe('testing form submission onSubmit', () => {
const testData = {
userId: '00000000',
password: 'SamplePassword0',
userType: 'Customer',
validForm: true,
}
it('should submit form onSubmit()', () => {
const mountedComponentHandle = mount(<LoginForm {...testData}/>);
const onSubmitForm = sinon.spy(
mountedComponentHandle.instance(),
'handleSubmitForm'
);
mountedComponentHandle.update();
const formHandle = mountedComponentHandle.find('form');
expect(formHandle.length).toBe(1);
formHandle.simulate('submit');
expect(onSubmitForm.called).toBe(true);
});
});
Please suggest on how to test .then() and .catch() of axios.
Thanks.
Key here is to make your code "testable". Separating responsibility helps to make your code more testable, readable and easy to maintain. In your case logic to post data over an API lies in some service which will handle api requests for your app, and you can test it separately.
Coming back to your question, I am providing you one of the possible solutions for testing async calls in your case:
// apiGateway.js
const postData = (url, data) => (
axios({
data: JSON.stringify(data),
type: 'post',
url: BASE_URL_SERVER + url,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
cache: false
})
);
Again you can test above code separately.
// myAppApi.js
const postLoginForm = (data, callback, errorCallback) => {
return postData('/rest/login', data)
.then((response) => callback(response.data))
.catch((error) => errorCallback(error))
};
// myAppApi.test.js
// import * as myAppApi from '../myAppApi'
it('should call callback when response is successful', async () => {
const mockResponse = {};
const mockRequestData = {};
const mockSuccessCallback = jest.fn();
const mockErrorCallback = jest.fn();
spyOn(myAppApi, 'postLoginForm').and.returnValue(Promise.resolve(mockResponse));
await myAppApi.postLoginForm(mockRequestData, mockSuccessCallback, mockErrorCallback);
expect(mockSuccessCallback).toHaveBeenCalled();
});
it('should call error callback when response is failed', async () => {
const mockRequestData = {};
const mockSuccessCallback = jest.fn();
const mockErrorCallback = jest.fn();
spyOn(myAppApi, 'postLoginForm').and.returnValue(Promise.reject());
await myAppApi.postLoginForm(mockRequestData, mockSuccessCallback, mockErrorCallback);
expect(mockErrorCallback).toHaveBeenCalled();
});
In above tests you can use different mocking methods or libraries.
And finally your component will look something like this
// LoginForm.js
class LoginForm extends React.Component {
onSuccessfulLogin(responseData) {
//.. success logic here
}
onFailedLogin(error) {
//.. error logic here
}
onSubmitForm(event) {
postLoginForm(this.state.data, this.onSuccessfulLogin, this.onFailedLogin)
}
}
As you can see separating out logic helps in testing. Further it will save you from ending up with component with tons of code in it. You can test your component for its state and presentation.
Hope this answers your question!
来源:https://stackoverflow.com/questions/45963781/react-testing-onsubmit-using-axios