Can't test asynchronous functions inside an action #1880
-
I make calls to an API but when I test the function, my test does not detect the calls correctly. API calls are made by an action that receives functions by parameters to execute them asynchronously. This is the action that calls multiple APIs. EX: async fetchContent(content: Function[]) {
this.changeLoadingState(true)
for (const runFunction of content) {
await runFunction()
}
this.changeLoadingState(false)
} This is an individual API action async getToken() {
const shouldRenew = this.expires_in < Date.now()
if (shouldRenew) {
const { data }: AxiosResponse<Resource> = await fetchToken()
const { token, expires_in } = data
const refreshAt = parseInt(expires_in.toString()) // Hablar con Noe para ver si puede enviarnos esta propiedad en Integer
const newExpire = refreshAt * 1000 + Date.now()
this.token = token
this.expires_in = newExpire
this.layout = LAYOUT.FUNNEL
}
} As you can see, in the action I call a function called "fetchToken", this is responsible for making the request to the API. import axios from 'axios'
import type { CancelType } from '@/types/auth'
import { settings } from '@/services/http/index'
const GET_TOKEN = 'getToken'
const TOKEN_URL = `/${import.meta.env.VITE_APP_API_TOKEN}`
const CancelToken = axios.CancelToken
export const cancelations: CancelType[] = []
export const fetchToken = () =>
settings.get(TOKEN_URL, {
cancelToken: new CancelToken((token) => {
cancelations.push({ token, service: GET_TOKEN })
})
}) When I execute that first action (getToken), it is supposed to be making a call to an API, which when it returns 200 should assign its data to the store. For that I created a helper with import MockAdapter from 'axios-mock-adapter'
import { beforeEach } from 'vitest'
import { settings } from '@/services/http'
const mock = new MockAdapter(settings, { delayResponse: 1000 })
beforeEach(() => {
mock.onGet('/coche/tarificador.getSPAToken/').reply(200, {
success: true,
token: 'baf25d48-34ea-3a94-aff9-88caafea25b8',
expires_in: '813',
message: 'OK'
})
}) Below is the entire test. import { flushPromises, mount } from '@vue/test-utils'
import { expect, it, vi } from 'vitest'
import { createTestingPinia } from '@pinia/testing'
import { i18n } from '@/i18n'
import { useVehicleStore } from '@/store/vehicle'
import { useResourceStore } from '@/store/resources'
import {
mock,
mockBrands,
mockFavoriteBrands
} from '@/test/mocks/vehicle'
import BrandStep from '@/components/common/vehicle/BrandStep/BrandStep.vue'
const pinia = createTestingPinia({
stubActions: false,
stubPatch: false,
fakeApp: true
})
const vehicleStore = useVehicleStore()
const resourceStore = useResourceStore()
let wrapper: any
describe('BrandStep component', () => {
beforeEach(() => {
wrapper = mount(BrandStep, {
global: {
plugins: [i18n, pinia]
}
})
})
afterEach(() => {
wrapper.unmount()
vi.resetAllMocks()
})
it('Should filter the brands options when input has a specific text', async () => {
const spy = vi.spyOn(mock, 'onGet')
mockFavoriteBrands()
mockBrands()
await flushPromises()
expect(resourceStore.token).toBe(2)
expect(vehicleStore.brands).toBe(1)
expect(spy).toHaveBeenCalledTimes(1)
const input = wrapper.find(
`[placeholder="${wrapper.vm.t('brand.placeholder')}"]`
)
await input.setValue('Tesla')
await nextTick()
const teslaModel = wrapper.find('.button-card__text').text()
expect(teslaModel).toBeTruthy()
})
}) I don't know what I might be doing wrong, the store data is not being assigned. It seems that the axios mock is not working. If we follow the logic of things, my "token" state should have the value: 'baf25d48-34ea-3a94-aff9-88caafea25b8' and yet it is still empty. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
If this has happened to any of you as well, the solution is quite simple.
const getMockToken = (mock: any) => {
mock.onGet('/car/tarifier.getSPAToken/').reply(200, {
success: true,
token: 'baf25d48-34ea-3a94-aff9-88caafea25b8',
expires_in: '813',
message: 'OK'
})
}
export default getMockToken In my case I have done it this way for the tokens and to use 3 instances at a time.. like this const vehicleMocks = () => {
http.defaults.baseURL = import.meta.env.SOME_ENV
const mockAuth = new MockAdapter(http)
const mockHttp = new MockAdapter(settings)
getMockToken(mockHttp)
mockHttp
.onGet('/SOME_ROUTE/')
.reply(200, tokenData)
mockHttp.onGet(API_FAVORITE_BRANDS).reply(200, favoriteBrandsData)
mockAuth
.onGet(API_BRANDS, {
vehicle_type_list: [100, 120, 150, 200, 250, 300, 310]
})
.reply(200, brandsData)
}
export default vehicleMocks Nowhere in the documentation will the axios-mock-adapter tell you that if you have X instances of Axios and export functions that use them, you will be overwriting them and thus damaging the spy to return the answers, so I suggest you ALWAYS do everything in one place or send the axios instance via function parameters. This way it will be solved, you can close and I hope this helps someone. |
Beta Was this translation helpful? Give feedback.
If this has happened to any of you as well, the solution is quite simple.
Remove this, only use if you have parameters
{ delayResponse: 1000 }
Create a helper that receives the mock from the axios instance and then call it in your component or wherever you need to do it.
In my case I have done it this way for the tokens and to use 3 instances at a time.. like this