Jest 'toHaveBeenCalled' for click events only works after two clicks? (Testing a Vue app)

Viewed 1857

A simple component:

<template>
  <div>
    <p>
      {{ count }}
    </p>
    <button @click="increment" data-test="increment">Increment</button>
  </div>
</template>
<script>
  export default {
    data () {
      return {
        count: 0
      }
    },
    methods: {
      increment () {
        this.count++
      }
    }
  }
</script>

And my test:

import TestExperiment from '@/components/TestExperiment'
import { createLocalVue, shallowMount } from '@vue/test-utils'

const localVue = createLocalVue()

describe('testexperiment.test.js', () => {
  const cmp = shallowMount(TestExperiment, {
    localVue
  })

  const increment = jest.spyOn(cmp.vm, 'increment')
  const incrementButton= cmp.find('[data-test="increment"]')

  test('clicking increment button calls increent', () => {

    expect(incrementButton.exists()).toBe(true)

    incrementButton.trigger('click')

    // Checking call here fails:
    // expect(increment).toHaveBeenCalled()

    // Function was still obviously called
    expect(cmp.vm.count).toBe(1)

    incrementButton.trigger('click')

    // Checking call here passes:
    expect(increment).toHaveBeenCalled()
  })

})

As you can see, I trigger a click on incrementButton twice.

After the first call, if I test if the method 'increment' was called, it returns false. However, count is indeed incremented. After the second call, it registers that it was in fact called (if I test how many times it was called, it asserts that it was called one time, even if count is 2, clearly having been incremented twice).

What am I missing about how Jest/Vue works?

1 Answers

You need to use the Vue Test Utils setMethod method:

const wrapper = mount(Foo)
const clickMethodStub = sinon.stub()

wrapper.setMethods({ clickMethod: clickMethodStub })
wrapper.find('button').trigger('click')

expect(clickMethodStub.called).toBe(true)

The reason it does not work in your example is because the element that you dispatch a click on has the original handler that the component created when it was instantiated.

It works after two calls to trigger because the initial call causes a re render, and the patched element has its handler updated to use the stub method that you added to the instance.

Related