Je teste un composant de connexion qui utilise Axios. J'ai essayé de me moquer d'Axios avec axios-mock-adapter
, mais lorsque j'exécute les tests, il reste des erreurs avec:
Error: Request failed with status code 404
Comment se moquer correctement d'Axios dans mes tests?
import Vue from 'vue'
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Login from '../../src/components/global/login/Login.vue';
import Raven from "raven-js";
import jQuery from 'jquery'
import Vuex from 'vuex'
import router from '../../src/router'
var axios = require('axios');
var MockAdapter = require('axios-mock-adapter');
describe('Login.vue', () => {
let wrapper;
let componentInstance;
let mock;
beforeEach(() => {
global.requestAnimationFrame = setImmediate,
mock = new MockAdapter(axios)
wrapper = shallowMount(Login, {
router,
$: jQuery,
attachToDocument: true,
mocks: {
$t: () => { },
Raven: Raven,
},
data() {
return {
email: '',
password: '',
}
}
})
componentInstance = wrapper.vm;
})
afterEach(() => {
mock.reset()
})
it('calls `axios()` with `endpoint`, `method` and `body`', async () => {
const formData = {
email: '[email protected]',
password: '111111'
};
let fakeData = { data: "fake response" }
mock.onPost(`${process.env.VUE_APP_BASE_URL}/login/`, formData).reply(200, fakeData);
wrapper.vm.email = '[email protected]';
wrapper.vm.password = '111111';
wrapper.vm.doSigninNormal()
})
})
doSigninNormal() {
const formData = {
email: this.email,
password: this.password
};
this.$v.$touch()
if (this.$v.$invalid ) {
this.loading = false;
this.emailLostFocus = true;
this.passwordLostFocus = true;
$('html, body').animate({scrollTop:110}, 'slow')
} else {
axios.post("/login", formData, {
headers: { "X-localization": localStorage.getItem("lan") }
})
.then(res => {
if (!res.data.result) {
if (res.data.errors) {
for (var i = 0; i < res.data.errors.length; i++) {
this.$toaster.error(res.data.errors[i].message);
if (
res.data.errors[0].message == "Your email is not yet verified"
) {
this.showVerificationLinkButton = true;
}
if (res.data.errors[i].field === "email") {
this.$toaster.error(res.data.errors[i].message);
}
if (res.data.errors[i].field === "password") {
this.$toaster.error(res.data.errors[i].message);
}
}
}
this.loading = false;
this.$v.$reset();
} else {
this.loading = false;
Raven.setUserContext({
email: res.data.user.email,
id: res.data.user.id
});
this.$store.dispatch("login", res);
this.$v.$reset();
}
})
.catch((err) => {
console.log('catch', err);
});
}
}
Le problème est sur le paquet axios-mock-adapter . Il nécessite une instance d'axios à l'aide de la méthode .create()
. Voir ici: création d'une instance
Dans votre App.js, utilisez:
import axios from "axios";
const instance = axios.create();
instance.post("http://localhost/api/user/update", {name: "Test"}, {headers: {"Authorization": "Bearer token")}});
Cependant, rien ne doit être changé dans les tests.
J'ai obtenu l'indice des tests de axios-mock-adapter .
Un exemple de ceci est: post test
Il existe deux façons simples de se moquer d'axios afin que vos tests n'effectuent pas de vraies requêtes http et utilisent à la place un objet simulé:
import axios from 'axios`;
Vue.component({
data() {
return {
axios,
}
},
methods: {
makeApiCall() {
return this.axios.post(...)
}
}
})
Vous pouvez donc facilement injecter une maquette dans vos tests:
it('test axions', function() {
const post = jest.fn();
const mock = {
post,
}
// given
const wrapper = shallowMount(myComponent, {
data: {
axios: mock,
}
});
// when
wrapper.vm.makeApiCall();
// then
expect(post).toHaveBeenCalled();
});
Je pense que c'est la manière la plus simple.
Vous pouvez configurer un plugin comme vue-plugin-axios pour injecter automatiquement axios dans chaque composant, comme:
makeApiCall(){
this.$axios.post(...)
}
Sans avoir besoin de le déclarer explicitement dans data
.
Ensuite, dans votre test, au lieu de passer la maquette dans le cadre de data
, vous la transmettez dans le cadre de mocks
, qui est la façon dont vue-test-utils
Traite les injections globales:
it('test axions', function() {
const post = jest.fn();
const mock = {
post,
}
// given
const wrapper = shallowMount(myComponent, {
mocks: {
$axios: mock,
}
});
// when
wrapper.vm.makeApiCall();
// then
expect(post).toHaveBeenCalled();
});
Voici comment se moquer des appels axios pour empêcher les appels réels axios et effectuer une véritable requête http.
Avec jest.fn
, Vous pouvez configurer une fonction factice pour renvoyer un objet spécifique, comme:
const post = jest.fn( () => ({status: 200, response: ...}) )
Vous pouvez également accéder aux paramètres de l'appel via hasBeenCalledWith' method, or more complex stuff via
Mock.calls` ( plus d'informations ici ):
expect(post).toHaveBeenCalledWith(expectedParams)
.
Donc, votre test final devrait être le suivant, je pense:
it('calls axios() with endpoint, method and body',async (done) => {
// given
const formData = { email: '[email protected]', password: '111111' };
const fakeResponse = {response: "fake response"};
const email = '[email protected]';
const uri = 'somepath/login/'; // I dont think you can access Vue process env variables in the tests, so you'll need to hardcode.
const password = '11111';
const post = jest.fn(() => Promise.resolve({status: 200}) );
const mock = {
post,
}
const wrapper = shallowMount(Component, {
data() {
return {
axios: mock,
// email,
// password, // you could do this instead to write to wrapper.vm later
}
}
});
wrapper.vm.email = '[email protected]';
wrapper.vm.password = '111111';
// when
await wrapper.vm.doSigninNormal();
// then
expect(post).toHaveBeenCalledWith({uri, password, email});
// or
const calls = post.mock.calls;
const firstParam = calls[0][0];
expect(firstParam.uri).toBe(uri);
expect(firstParam.email).toBe(email);
expect(firstParam.password).toBe(password);
done();
});
Si l'adaptateur d'instance axios (xhr ou http) est repris par axios-mock-adapter, il y aura une erreur avec une mauvaise configuration baseURL comme ceci:
{baseURL:'/for/bar'}
Si nous envoyons une demande comme:
get('/api/v1/exampleService')
La dernière requête http deviendra
'http://Host:port/for/bar/for/bar/api/v1/exampleService'
Étant donné que l'adaptateur factice prend le relais de l'adaptateur par défaut axios, les API qui ne correspondent pas aux règles factices seront transmises et traitées par l'adaptateur par défaut, ces deux logiques de sélection d'adaptateur passent par ici (core/dispatchRequest.js):
if (config.baseURL && !isAbsoluteURL(config.url)) {
config.url = combineURLs(config.baseURL, config.url);
}
Ainsi, si vous utilisez une maquette, veuillez utiliser l'URL complète commence par http: //