in Vue , I have a util class where i abstracted the axios invocation and bit of logic..
import Axios from 'axios'..the code almost looks like this
export default {
getStudentNumber (name) {
Axios.post('myurl', { studentName: name }).then({
//some logic
//return
})
}
}
This is being invoked from my Vue class... I wrote jest test cases for Vue and I mocked the Axios in that...But is there a way to write separate test cases for this service class ? How to write it? Becuase I have a lot of logic in this... I am using jest
You can write the test for your service like this:
import Axios from 'axios';
import myService from './myService';
jest.mock('axios');
describe('my service', () => {
describe('getStudentNumber', () => {
it('should call Axios.post with myurl and studentName', () => {
myService.getStudentNumber('mock name');
expect(Axios.post).toHaveBeenCalledWith('myurl', { studentName: 'mock name' })
});
describe('given a successful response', () => {
beforeAll(() => {
// setup mock for successful flow
Axios.post.mockResolvedValue({ responseMock });
});
it('should do this', () => {
const result = myService.getStudentNumber();
// describe the expected result
expect(result).toEqual({ ...... });
});
});
describe('given an error', () => {
beforeAll(() => {
// setup mock for non-successful flow
Axios.post.mockRejectedValue(new Error('some mock error'));
});
it('should do that', () => {
const result = myService.getStudentNumber();
// describe the expected result
expect(result).toEqual({ ...... });
});
});
});
});
Related
I have the following problem while trying to unit test my Vue application.
Even spying and mocking $router.push, I still can't make it to be called while inside unit testing:
This is my unit testing (Home.spec.js)
const localVue = createLocalVue();
localVue.use(Vuex);
localVue.use(VueRouter);
describe('Home.vue', () => {
let actions;
let getters;
let store;
let router;
beforeEach(() => {
actions = {
[FETCH_USER_REPOSITORIES]: jest.fn()
};
getters = {
getTopThreeRepositories: jest.fn(repositoryMock.getRepositories)
};
store = new Vuex.Store({ getters, actions });
router = new VueRouter();
});
it('should redirect when 404 status code received', async () => {
jest.spyOn(store, 'dispatch').mockRejectedValue({ statusCode: 404 });
jest.spyOn(router, 'push').mockResolvedValue({});
const wrapper = await shallowMount(Home, {
store,
localVue,
router
});
expect(router.push).toHaveBeenCalledWith('/not-found');
});
});
Now, this is my Home.vue:
import { mapGetters } from 'vuex';
import { FETCH_USER_REPOSITORIES } from "../store/actions";
import RepositoryList from "#/components/RepositoryList";
import Card from "#/components/Card";
export default {
name: 'view-home',
components: {
Card,
RepositoryList
},
async beforeMount() {
try {
await this.$store.dispatch(FETCH_USER_REPOSITORIES, 'some-repo');
} catch(err) {
console.log(this.$router);
await this.$router.push('/not-found');
}
},
computed: {
...mapGetters(["getTopThreeRepositories"])
}
}
The console log shows the $router correctly, with the spy.
If I force the calling inside the unit testing, it works, but the expects always fails giving me back that $router.push hasn't been called.
Can anyone help me, please?
Thanks!
You should specify $store and $route as mocks in your mounting options, as shown below. Also there's no need to await shallowMount because shallowMount does not return a Promise, so the await would just return immediately.
describe('Home.vue', () => {
it('should redirect when 404 status code received', () => {
const $store = {
dispatch: jest.fn()
}
const $router = {
push: jest.fn()
}
const wrapper = shallowMount(Home, {
localVue,
mocks: {
$store,
$router,
}
});
expect($router.push).toHaveBeenCalledWith('/not-found');
})
})
I am trying to mock a GET request to get some Posts using the ID. This is the code I am trying to mock:
getPost() {
this.refreshToken();
http
.get(`/posts/${this.$cookie.get('postid')}`, {
headers: {
"Authorization": `Bearer ${this.$cookie.get('token')}`,
"Content-type": "application/json",
},
})
.then((response) => {
this.post = response.data;
})
.catch((error) => {
console.log(error.response);
});
}
This is my attempt at a test:
import {getPost} from '#/views/Post.vue'
import axios from 'axios';
jest.mock('axios');
describe('get Post by ID', () => {
afterEach(() => {
jest.resetAllMocks();
});
it('should return empty when axios.get failed', async () => {
const getError = new Error('error');
axios.get = jest.fn().mockRejectedValue(getError);
const actualValue = await getPost();
expect(actualValue).toEqual(new Map());
expect(axios.get).toBeCalledWith('/posts/postid');
});
it('should return users', async () => {
const mockedUsers = [{ postID: 1 }];
axios.get = jest.fn().mockResolvedValue(mockedUsers);
const actualValue = await getPost(['1']);
expect(actualValue).toEqual(mockedUsers);
expect(axios.get).toBeCalledWith('/posts/postid');
});
})
The error I am getting is:
TypeError: (0 , _Post.getPost) is not a function
I am not sure what to do, and any help would be super appreciated. Thanks!
Assuming you have getPost() defined in the Post component's methods, you can't use named imports to access getPost. Instead, you'll have to mount the component, and use the wrapper's vm:
// Post.spec.js
import { shallowMount } from '#vue/test-utils'
import Post from '#/views/Post.vue'
it('...', () => {
const wrapper = shallowMount(Post)
await wrapper.vm.getPost()
expect(wrapper.vm.post).toEqual(...)
})
Also make sure to return the axios call in getPost() so that it could be awaited:
// Post.vue
export default {
methods: {
getPost() {
this.refreshToken();
👇
return http.get(/*...*/)
.then(/*...*/)
.catch(/*...*/);
}
}
}
I want to test my action.
My action has 1 AXIOS request and 1 mutation.
I mocked my axios with jest.mock(“axios” …). I return a Promise.
I defined a mock value with the method mockResolvedValue().
But when I test if my axios.post is called with the method toHaveBeenCalledTimes(1), Jest tells me 0 time.
My test:
My action:
My error:
Someone knows why my axios.post is not called ?
This is the solution:
import actions from '#/store/actions'
import mutations from '#/store/mutations'
import state from '#/store/state'
import store from '#/store'
import axios from 'axios'
let url = ''
let body = {}
jest.mock("axios", () => ({
post: jest.fn((_url, _body) => {
return new Promise((resolve) => {
url = _url
body = _body
resolve(true)
})
})
}))
//https://medium.com/techfides/a-beginner-friendly-guide-to-unit-testing-the-vue-js-application-28fc049d0c78
//https://www.robinwieruch.de/axios-jest
//https://lmiller1990.github.io/vue-testing-handbook/vuex-actions.html#testing-actions
describe('getGameList', () => {
test('Success: should return the game list of the user and update gameList in the store', async () => {
const context= {
state: {
user: {
id:1
}
},
commit: jest.fn()
}
const response = {
data: [
{ id:1, name:"game_name1" },
{ id:2, name:"game_name2" }
]
};
axios.post.mockResolvedValue(response) //OR axios.post.mockImplementationOnce(() => Promise.resolve(response));
await actions.getGameList(context)
expect(axios.post).toHaveBeenCalledWith("api/game_list_of_user",{"user_id":1});
expect(axios.post).toHaveBeenCalledTimes(1)
expect(context.commit).toHaveBeenCalledWith("UpdateGameList", response.data)
});
test('Error: an error occurred', () => {
const errorMessage = 'Error';
axios.post.mockImplementationOnce(() =>
Promise.reject(new Error(errorMessage))
);
});
});
I have seen similar questions but they dont actually address what am looking for.
so am using using axios globally in app.js for my vue app like window.axios=require('axios')
then in auth.js i have this
export function login(credentials){
return new Promise((res,rej) => {
axios.post('/api/auth/login', credentials)
.then((response) => {
res(response.data);
})
.catch((err) => {
rej("Wrong email or password");
})
});
}
which works fine on the login page
however in my test script
jest.mock("axios", () => ({
post: jest.fn(() => Promise.resolve({data:{first_name:'James','last_name':'Nwachukwu','token':'abc123'}}))
}));
import axios from 'axios'
import {login} from '../helpers/auth'
it("it logs in when data is passed", async () => {
const email='super#gmail.com'
const password='secret';
const result=await login({email,password});
expect(axios.post).toBeCalledWith('/api/auth/login',{"email": "super#gmail.com", "password": "secret"})
expect(result).toEqual({first_name:'James','last_name':'Nwachukwu','token':'abc123'})
})
shows axios is not defined
but if i change auth.js to
import axios from 'axios'
export function login(credentials){
return new Promise((res,rej) => {
axios.post('/api/auth/login', credentials)
.then((response) => {
res(response.data);
})
.catch((err) => {
rej("Wrong email or password");
})
});
}
test passes. how do i run the test without having to import axios on each vue file
I had the same problem just now. I am also including axios via window.axios = require('axios'); in my app.js.
The solution is to set your axios mock on window.axios in your test. So instead of this (incorrect):
axios = {
post: jest.fn().mockName('axiosPost')
}
const wrapper = mount(Component, {
mocks: {
axios: axios
}
})
When your component code calls axios.whatever it is really calling window.axios.whatever (as I understand it), so you need to mirror that in your test environment:
window.axios = {
post: jest.fn().mockName('axiosPost')
}
const wrapper = mount(Component, {
mocks: {
axios: window.axios
}
})
And in your test:
expect(window.axios.post).toHaveBeenCalled()
The above method works fine until you want to chain then to it. In which case you need to set your mock up like this:
window.axios = {
get: jest.fn(() => {
return {
then: jest.fn(() => 'your faked response')
}
}),
}
You don't need to pass it into the component mock though, you can just mount (or shallowMount) the component as usual
I am trying to mock an axios call within a vuejs method. Is this possible?
Here is my vue component (SomeObj):
methods:{
callAxiosMethod() {
const callApi= axios.create();
callApi.defaults.timeout = 10000;
callApi.get(mockedUrl)
.then(response => {
console.log('response is ' + response);
})
.catch(e => {});
}
}
Here is my spec.js
let mockData = {};
beforeEach(() => {
jest.spyOn(axios, 'get').mockReturnValue(Promise.resolve(mockData));
});
let wrapper = shallowMount(SomeObj, {
stubs: [], localVue, mocks: {
mockUrl: mockUrl,
$route: {
params: { testId: "123" }
}
}
});
it('is a Vue instance', () => {
expect(wrapper.isVueInstance()).toBeTruthy();
axios.get.mockResolvedValue(mockData);
wrapper.vm.callAxiosMethod();
})
When I looked at the coverage, the system says the callApi is not covered. Any idea on how I can mock the axios call within the function?
Your code calls axios.create so you need to mock that function to return a mock callApi object.
Here is a simplified working example:
code.js
import * as axios from 'axios';
const mockedUrl = 'http://mock-url';
export const callAxiosMethod = () => {
const callApi = axios.create();
callApi.defaults.timeout = 10000;
return callApi.get(mockedUrl); // <= return the Promise so it can be awaited
}
code.test.js
import { callAxiosMethod } from './code';
jest.mock('axios', () => ({
create: jest.fn().mockReturnValue({
defaults: {},
get: jest.fn().mockResolvedValue('mocked data')
})
}));
test('callAxiosMethod', async () => { // <= async test function
const response = await callAxiosMethod(); // <= await the Promise
expect(response).toBe('mocked data'); // Success!
});