Search code examples
vue.jsunit-testingaxiosjest-mock-axios

Why isn't Axios method running?


I have the following vue app that allows a user to login. I am running a unit test that should login but axios won't run when the submit method is triggered.

The Submit method being called by button trigger in the test:

methods: {
submit() {

  var name = this.username.value.replace(/ /g, '%20')
  var url = 'http://localhost:8080/properties'

  axios.get(
    url,  
    {      
    auth: 
    {
      username: name, 
      password: this.password.value
      
    }})
    .then((res) =>{
      if(res.data.CurMember.name == this.username.value 
      && name !='' && this.password != '')
      {
        this.navigateToHome()
      }
      else
      {
        this.invalidLogin = true;
      }
    })
    .catch((error) =>{
      console.error(error)
    })
  },

navigateToHome() {
  this.$router.push({name: "HomePage"});
},

The test:

import BasicLogin from '@/views/BasicLogin.vue'
import {shallowMount, mount, flushPromises} from "@vue/test-utils"
import { createRouter, createWebHistory } from 'vue-router'
import axios from 'axios'

const mockDataCorrectLogin = 
        [{
            'authType': 'string',
            'curMember': {
                'name': 'bossman',
                'pass': 'bigboss!!',
                'role': 'string'
            },
            'version': "string"
        }]

describe('BasicLogin.vue', () => 
{
let wrapper = null

beforeEach(() => {
    wrapper = mount(BasicLogin, 
    {
        propsData:
        {
           //data go here
           usernameValue: '',
           passwordValue: '',
           
        },
    })
}),

it('Login as bossman, validate routing worked', async () => {
    
    const mockRoute = {
        params: {
            id: 1
        }
    }
    const mockRouter = {
        push: jest.fn()
    }
    const wrapper = mount(BasicLogin, {
        global: {
            mocks: {
                $route: mockRoute, 
                $router: mockRouter
            }
        }
    })


    const inputFieldUser = wrapper.get('[type="text"]').element
    inputFieldUser.value = 'bossman'
    expect(inputFieldUser.value).toBe('bossman')

    const inputFieldPass = wrapper.get('[type="password"]').element
    inputFieldPass.value = 'bigboss!!'
    expect(inputFieldPass.value).toBe('bigboss!!')

  
    jest.mock('axios', () => ({
        get: () => Promise.resolve(mockDataCorrectLogin)
    }))

    //This is where the submit is being called
    await wrapper.vm.submit();

    await flushPromises()
    expect(mockRouter.push).toHaveBeenCalledTimes(1)
    expect(mockRouter.push).toHaveBeenCalledWith({"name": "HomePage"})
    
 })
})

So why is the axios call being totally ignored in the submit method? This is the error message displayed mockRouter was never pushed because the axios call was never made

 FAIL tests/unit/BasicLogin.spec.js
 ● BasicLogin.vue › Login as bossman, validate routing worked

expect(jest.fn()).toHaveBeenCalledTimes(expected)

Expected number of calls: 1
Received number of calls: 0

88 |
89 |         await flushPromises()
> 90 |         expect(mockRouter.push).toHaveBeenCalledTimes(1)
 |                                 ^
91 |         expect(mockRouter.push).toHaveBeenCalledWith({"name": "HomePage"})
92 |         
93 |     })

at Object.<anonymous> (tests/unit/BasicLogin.spec.js:90:33)

Any help would be much appreciated.


Solution

  • jest.mock is hoisted in order to affect imports. In order for jest.mock to affect top-level import, it should be located at top level outside a test, it cannot be hoisted higher than a scope where it's used.

    Functions exports need to be mocked with jest.fn(), the implementation can be changed per test with spy API:

    axios.get.mockResolvedValue(mockDataCorrectLogin)
    

    If a mock is needed per test, jest.mock can be located inside a test, a module needs to be be re-imported after it. This is only needed if non-function exports need to be mocked, or a module produces side effects that need to be reapplied.