bencodezen / vue-enterprise-boilerplate

An ever-evolving, very opinionated architecture and dev environment for new Vue SPA projects using Vue CLI.
7.77k stars 1.32k forks source link

Cannot mock functions inside the mounting step in unit tests. #147

Closed aaroncoville closed 4 years ago

aaroncoville commented 5 years ago

First, thank you so much for this boilerplate. I've used a number of starting point projects for Vue apps in the past and this is by far the best. Now, on to my question...

I'm not certain if this is related to your specific configuration or more related to Jest or Vue Test Utils but I figured I'd start here.

With regards to mocking functions inside a component, if I try to stub out methods on my component as part of the shallow mounting step, e.g.:

function mountComponent() {
  return shallowMountView(Component, {
     methods: {
       getFoo: jest.fn()
     }
  })
}

And write a test like:

 it('Gets foo on initialization', async () => {
    const { vm } = mountComponent()

    await vm.onLoad()
    expect(vm.getFoo.mock.calls.length).toBe(1)
  })

I get an error of Cannot read property 'calls' of undefined on the 'expect' line.

To be clear the 'onLoad()' is not a vue lifecycle hook but a callback when a child component inside 'Component' is ready. Specifically in my use case this is 'onMapLoad' for mapbox-gl. The 'onLoad' method simply calls 'getFoo' inside the component.

    async onLoad() {
      await this.getFoo()
    }

I can get this scenario to work by removing the mock function from the mount and mocking it directly in the test like so:

  it('Gets foo on initialization'', async () => {
    const { vm } = mountComponent()

    vm.getFoo = jest.fn()

    await vm.onLoad()
    expect(vm.getFoo.mock.calls.length).toBe(1)
  })

function mountComponent() {
  return shallowMountView(Component)
}

But given that this function is called on initialization it needs to be mocked for almost every test, so you can see why I'd prefer to mock it during the mounting step. I'm probably doing something just ever-so-slightly wrong. Any ideas?

chrisvfritz commented 5 years ago

Instead of methods, I think you're looking for mocks.

And as a sidenote, I usually prefer:

expect(vm.getFoo).toHaveBeenCalledTimes(1)

to:

expect(vm.getFoo.mock.calls.length).toBe(1)

because the errors are slightly more clear. Hope that helps. 🙂

aaroncoville commented 5 years ago

I'm not sure... the mock documentation there is pretty sparse. It says it's useful for mocking global injections which this is not. Do you have an example of how I could implement a mock for a component method?

FWIW: On the same page you link to, at the very bottom other options is where I got the source for my attempt. Where it seems to state that the methods would override those of the component.

chrisvfritz commented 5 years ago

Ah, I see what you mean now. I'm honestly not sure why it's not working, but I can take a closer look if you provide a simple reproduction, either in a repo or in CodeSandbox. 🙂

chrisvfritz commented 4 years ago

Closing due to inactivity.