I'm trying to complete some lightweight tests based on render output focussing on initial load and user interaction.
I am working on a HTTP test for a login component. As you would expect, it's a basic component containing an email field, a password and a button. When the user clicks the button, it calls a method which makes an axios call to an endpoint. The specific test case that I am working on is the "invalid submission" test where I simulate a user simply hitting the submit button without entering any information, expecting the application to output errors.
I first tried going down the vue-test-utils route and triggering a button click and using Jest's spyOn
function to watch for axios.post
requests. Despite various attempts to delay using await
and various other suggested Promise
approaches, nothing was being rendered and spyOn
was not reporting any form of axios
calls, so the test failed despite following the documentation.
My research into Vue HTTP testing patterns led me to Mock tests. I had some success with Mock tests using axios-mock-adapter with the expect()
cases checking the axios
calls, however any sort of check on the render itself would fail. The mock tests had no affect on the vdom, despite having the mount
configured to attachTo: document.body
and various attempts to delay the checks with promises.
describe("when API call is unsuccessful", () => {
it('displays error messages', async () => {
let spy = jest.spyOn(axios, "post");
let result = null;
mock.onPost(url).reply(422, mockResult);
await axios.post(url).then(function (response) {
result = response
}).catch((error) => {
result = error.response.data
});
expect(spy).toHaveBeenCalledTimes(1) <-- Passes
expect(result).toEqual(mockResult) <-- Passes
expect(wrapper.get('.error--text')) <-- Fails
})
})
For the sake of trial & error, I even added a line into the catch function that set the wrapper.vm.errors
data property to the result
variable (which is what the component does following a failed request) and then the dom checks passed.
Am I missing something? Is there a clear, simple way of mocking an HTTP request and updating a component accordingly that I am just missing?
CodePudding user response:
I had a small breakthrough with this. I switched over to Moxios and had success with mocking HTTP requests that impact the component and its render.
login.spec.js:
import { mount, createLocalVue } from '@vue/test-utils';
import VueCompositionApi from '@vue/composition-api'
import axios from 'axios';
import Vuetify from "vuetify";
import moxios from 'moxios';
import expect from 'expect';
beforeEach(() => {
moxios.install()
const localVue = createLocalVue();
localVue.use(VueCompositionApi);
vuetify = new Vuetify();
jest.spyOn(axios, 'post').mockResolvedValue(mockResult)
wrapper = mount(localVue.component('login', require('../../resources/js/components/Login.vue').default), {
vuetify,
attachTo: document.body
});
})
afterEach(function () {
moxios.uninstall()
})
afterAll(() => {
wrapper.destroy()
})
describe("when API call is unsuccessful", () => {
it('displays error messages', (done) => {
wrapper.find('button').trigger('click')
moxios.wait(function () {
let request = moxios.requests.mostRecent()
request.respondWith({
status: 422,
response: mockResult
}).then(function(response){
done()
expect(response).rejects
expect(wrapper.get('.error--text'))
})
})
})
})