'How to test Vue Component method call within an async method

I believe I am struggling to properly mock my methods here. Here is my situation, I have a component with two methods;

    name: 'MyComponent',
    methods: {
       async submitAction(input) {
           // does await things
           // then ...
           this.showToastMessage();
       },
       showToastMessage() {
           // does toast message things
       },
    }

And I want to write a test that will assert that showToastMessage() is called when submitAction(input) is called. My basic test looking something like this;

    test('the toast alert method is called', () => {
      let showToastMessage = jest.fn();
      const spy = jest.spyOn(MyComponent.methods, 'showToastMessage');
      const wrapper = shallowMount(MyComponent, { localVue });

      const input = // some input data
      wrapper.vm.submitAction(input); // <--- this calls showToastMessage
      expect(spy).toHaveBeenCalled();
    };

NOTE: localVue is declare as such at the top of the file const localVue = createLocalVue();

I confirmed that both submitAction() and showToastMessage() methods are being called during the tests, by sneaking a couple of console.log()'s and observing it in the test output, however the test still fails;

    expect(jest.fn()).toHaveBeenCalledWith(...expected)

    Expected: called with 0 arguments

    Number of calls: 0

      566 |           const wrapper = shallowMount(MyComponent, { localVue } );
      567 |           wrapper.vm.submitAction(input);
    > 568 |           expect(spy).toHaveBeenCalledWith();

I've tried spying on both methods as well

    const parentSpy = jest.spyOn(MyComponent.methods, 'submitAction');
    const spy = jest.spyOn(MyComponent.methods, 'showToastMessage');
    // ...
    expect(spy).toHaveBeenCalled()

same results, test fail.

What am I missing?

Tech Stack: vue 3, jest, node 14



Solution 1:[1]

Took me a minute to realize/try this, but looks like since my calling function is async that I was suppose to make my test async, and await the main method call. This seems to have done the trick. Here's what ended up being my solution:

    test('the toast alert method is called', async () => {
      let showToastMessage = jest.fn();
      const spy = jest.spyOn(MyComponent.methods, 'showToastMessage');
      const wrapper = shallowMount(MyComponent, { localVue });

      const input = // some input data
      await wrapper.vm.submitAction(input);
      expect(spy).toHaveBeenCalled();
    };

Solution 2:[2]

@TekkSparrow you can pass a heap of stuff into the shallowMount function. It accepts an object as a second argument which can look something like

import { shallowMount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'

const localVue = createLocalVue()
localVue.use(Vuex)

let mocks = {
  // this could be something like the below examples
  // I had in a previous project
  $route: {
    query: '',
    path: '/some-path'
  },
  $router: [],
  $validator: {
    validateAll: jest.fn()
  },
  $toast: {
    show: jest.fn(),
    error: jest.fn()
  },
}
let propsData = {
  // some props you want to overwrite or test.
  // needs to be called propsData
}
let methods = {
  showToastMessage: jest.fn()
}
let store = new Vuex.Store({
  actions: {
    UPLOAD_ASSET: jest.fn(),
  },
})

const wrapper = shallowMount(MyComponent, { mocks, propsData, methods, store, localVue })

I believe that by doing similar to the above, your mocked function will run and be recorded by the Jest spy.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 TekkSparrow
Solution 2 RuNpiXelruN