web-dev-qa-db-fra.com

Erreur de capture Axios Échec de la demande avec le code d'état 404

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?

login.spec.js:

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()
  })
})

Login.vue

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);
    });
  }
}
6
priyeshvadhiya

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

1
aitchkhan

Axios moqueurs:

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é:

définir axios comme propriété de composant:

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.

Utilisez un plugin pour injecter des axios dans chaque composant

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.

Configuration du comportement fictif et des paramètres d'appel d'accès

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();

});
1
Sergeon

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: //

1
authur.wang