Axiosを使用するログインコンポーネントをテストしています。 axios-mock-adapter
を使用してAxiosをモックしようとしましたが、テストを実行すると、次のエラーが発生します。
Error: Request failed with status code 404
テストでAxiosを適切にモックする方法を教えてください。
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);
});
}
}
問題は 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
Axiosをモックする2つの簡単な方法があるので、テストは実際のhttpリクエストを実行せず、代わりにモックオブジェクトを使用します。
_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();
});
_
これが最も簡単な方法だと思います。
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();
});
_
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を使用してください