web-dev-qa-db-ja.com

Axiosキャッチエラーリクエストがステータスコード404で失敗しました

Axiosを使用するログインコンポーネントをテストしています。 axios-mock-adapterを使用してAxiosをモックしようとしましたが、テストを実行すると、次のエラーが発生します。

Error: Request failed with status code 404

テストでAxiosを適切にモックする方法を教えてください。

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

問題は axios-mock-adapter パッケージにあります。 .create()メソッドを使用するaxiosのインスタンスが必要です。ここを参照してください: インスタンスの作成

App.jsで次を使用します。

import axios from "axios";
const instance = axios.create();

instance.post("http://localhost/api/user/update", {name: "Test"}, {headers: {"Authorization": "Bearer token")}});

ただし、テストでは何も変更する必要はありません。

axios-mock-adapter のテストからヒントを得ました。

その例は次のとおりです。 post test

1
aitchkhan

Axiosのモック:

Axiosをモックする2つの簡単な方法があるので、テストは実際のhttpリクエストを実行せず、代わりにモックオブジェクトを使用します。

axiosをコンポーネントプロパティとして設定します。

_import axios from 'axios`;
Vue.component({
  data() {
    return {
      axios,
    }
  },
  methods: {
    makeApiCall() {
      return this.axios.post(...)
    }
  }
})
_

したがって、テストにモックを簡単に挿入できます。

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

これが最も簡単な方法だと思います。

プラグインを使用してすべてのコンポーネントにaxiosを挿入する

vue-plugin-axios のようなプラグインを設定して、次のようにaxiosをすべてのコンポーネントに自動的に挿入できます。

_  makeApiCall(){
    this.$axios.post(...)
  }
_

dataで明示的に宣言する必要はありません。

次に、テストで、モックをdataの一部として渡す代わりに、mocksの一部として渡します。これは、_vue-test-utils_がグローバルインジェクションを処理する方法です。

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

これは、実際のaxiosの呼び出しを防ぎ、実際のhttpリクエストを実行するためにaxios呼び出しをモックする方法です。

モック動作の構成と呼び出しパラメーターのアクセス

_jest.fn_を使用すると、次のように特定のオブジェクトを返すようにモック関数を設定できます。

const post = jest.fn( () => ({status: 200, response: ...}) )

_hasBeenCalledWith' method, or more complex stuff via_ mock.calls`を介して呼び出しのパラメーターにアクセスすることもできます( 詳細はこちら ):

expect(post).toHaveBeenCalledWith(expectedParams)

したがって、最終的なテストは次のようになるはずです。

_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

Axiosインスタンスアダプター(xhrまたはhttp)がaxios-mock-adapterに引き継がれた場合、次のような不正なbaseURL構成でエラーが発生します。

{baseURL:'/for/bar'} 

次のようなリクエストを送信した場合:

get('/api/v1/exampleService')

最後のhttpリクエストは次のようになります

'http://Host:port/for/bar/for/bar/api/v1/exampleService'

モックアダプターがaxiosのデフォルトアダプターを引き継ぐため、モックルールに一致しないAPIはパススルーされ、デフォルトのアダプターで処理されます。これらのアダプター選択ロジックは両方ともここを通過します(core/dispatchRequest.js):

if (config.baseURL && !isAbsoluteURL(config.url)) { 
   config.url = combineURLs(config.baseURL, config.url);
}

したがって、モックを使用する場合は、http://で始まる完全なURLを使用してください

1
authur.wang