'Vue3- Using Vitest toHaveBeenCalled() method
I am running a vue3 application using the Composition API and the setup()
hook.
I am using Vitest as unit-test framework. (v 0.6.1)
I have the following sample component :
// src/components/MyComponent.vue
<template>
<div>
<h1>counter : {{ counter }}</h1>
<button
@click="incrementCounter"
>
Click
</button>
</div>
</template>
<script setup lang="ts">
// imports
import { ref } from 'vue'
// datas
const counter = ref(1)
// methods
const incrementCounter = () => {
if (confirm()) { // call the confirm method
counter.value++ // increment counter by 1
}
}
const confirm = () => {
return true
}
</script>
And its test file :
// src/components/MyComponent.spec.ts
import {
shallowMount
} from '@vue/test-utils'
import MyComponent from '@/components/MyComponent.vue'
describe('component/MyComponent.vue', () => {
it('incrementCounter method', () => {
const wrapper = shallowMount(MyComponent) // create the wrapper
const confirmSpy = vi.spyOn(wrapper.vm, 'confirm') // create the confirm method spy
wrapper.vm.incrementCounter() // use the incrementCounter method
expect(wrapper.vm.counter).toBe(2) // test passed
expect(confirmSpy).toHaveBeenCalled() // test failed
})
})
The goal of the test is simply to verify if the confirm()
method has been called inside the incrementCounter()
method or not.
I tried to use the vitest tohavebeencalled() method with a spy of the confirm()
method but the test end up in failure with the following message:
Re-running tests... [ src/components/MyComponent.spec.ts ]
× src/components/MyComponent.spec.ts > component/MyComponent.vue > incrementCounter method → expected "confirm" to be called at least once
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL src/components/MyComponent.spec.ts > component/MyComponent.vue
incrementCounter method AssertionError: expected "confirm" to be called at least once ❯ src/components/MyComponent.spec.ts:13:23 11| wrapper.vm.incrementCounter() // use the incrementCounter method 12| expect(wrapper.vm.counter).toBe(2) // test passed 13| expect(confirmSpy).toHaveBeenCalled() // test failed | ^ 14| }) 15| })
It seems to indicate that confirm()
method has not been called, but since the counter value has been increased to 2, I guess it implies that the method has been effectively called in fact.
I am using the spyOn()
method wrong? What should I do to make this test pass?
Thank you in advance for your help.
Solution 1:[1]
In this case, the reference to confirm
in incrementCounter()
cannot be externally modified. That's as impossible as modifying a function's private variables.
Here's a similar vanilla JavaScript example that demonstrates what the code in your <script setup>
is attempting (run the snippet below for demo):
function setup() {
const incrementCounter = () => {
confirm()
}
const confirm = () => {
console.log('confirm')
}
return {
incrementCounter,
confirm,
}
}
const comp = setup()
comp.incrementCounter()
console.log('trying to update confirm...')
comp.confirm = () => console.log('Yes do it!')
comp.incrementCounter() // ? still calls original confirm
However, the properties of an object can be modified externally. So, a workaround is to update incrementCounter()
to reference confirm
via an object, which we'll later update:
function setup() {
const incrementCounter = () => {
/*?*/
ctx.confirm()
}
const confirm = () => {
console.log('confirm')
}
/*?*/
const ctx = {
incrementCounter,
confirm,
} /*?*/
return ctx
}
const comp = setup()
comp.incrementCounter()
console.log('trying to update confirm...')
comp.confirm = () => console.log('Yes do it!')
comp.incrementCounter() // ? calls new confirm above
Using that same technique, incrementCounter()
can refer to confirm
via an object in <script setup>
:
<!-- MyComponent.vue -->
<script setup>
import { ref } from 'vue'
const counter = ref(1)
const incrementCounter = () => {
/*?*/
if (ctx.confirm()) {
// call the confirm method
counter.value++ // increment counter by 1
}
}
const confirm = () => {
return true
}
/*?*/
const ctx = {
confirm
}
</script>
Now, you can use spyOn
on ctx.confirm
:
// MyComponent.spec.js
import { describe, it, expect, vi } from 'vitest'
import { shallowMount } from '@vue/test-utils'
import MyComponent from '@/components/MyComponent.vue'
describe('component/MyComponent.vue', () => {
it('incrementCounter method', () => {
const wrapper = shallowMount(MyComponent)
/*?*/
const confirmSpy = vi.spyOn(wrapper.vm.ctx, 'confirm')
wrapper.vm.incrementCounter()
expect(wrapper.vm.counter).toBe(2)
expect(confirmSpy).toHaveBeenCalled() /*?*/
})
})
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 |