This issue gives me a hard time and I can't understand how to make Vue Test Utils and BootstrapVue play nice together.
A minimal example would look like this:
MyComponent.vue
<template>
<div>
<b-button variant="primary" @click="play">PLAY</b-button>
</div>
</template>
<script>
export default {
name: 'MyComponent',
methods: {
play() {
console.log("Let's play!");
}
}
}
</script>
In the main.js
we use BootstrapVue: Vue.use(BootstrapVue)
.
This is how I'm trying to test that the click
event has been triggered:
import { expect } from 'chai';
import sinon from 'sinon';
import Vue from 'vue';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import BootstrapVue, { BButton } from 'bootstrap-vue';
import MyComponent from '@/components/MyComponent.vue';
const localVue = createLocalVue();
localVue.use(BootstrapVue);
describe('MyComponent.vue', () => {
it('should call the method play when button is clicked', () => {
const playSpy = sinon.spy();
const wrapper = shallowMount(MyComponent, {
localVue,
methods: {
play: playSpy,
},
});
wrapper.find(BButton).trigger('click');
expect(playSpy.called).to.equal(true);
});
});
This gives me:
AssertionError: expected false to equal true + expected - actual -false +true
I checked How to test for the existance of a bootstrap vue component in unit tests with jest?, but it doesn't apply to BButton
.
When running the test I also don't see any output on the command line, where I would expect this line to be executed:
console.log("Let's play!");
What's wrong here?
We can trigger events on an element with Vue. js by assigning a ref to the element we want to trigger events for. Then we can call methods on the element assigned to the ref to trigger the event.
The plugin pulls all required dependencies (including jest), creates a jest. config. js file with sensible defaults, and generates a sample test suite. After that, all you need to do is to install Vue Test Utils.
The reason why the event click
couldn't be triggered is the way how shallowMount
works in contrast to mount
.
As we know, Vue Test Utils provide two methods to mount a component, i.e. render the template and generate a DOM tree:
The first method mount
generates a complete DOM tree and traverses through all child components. Most of the time this is not necessary, so the method shallowMount
is preferred - it stubs the child components just one level below the parent component.
In my case this was also the root of the problem. BootstrapVue provides components, like BButton
, which can be used in your own Vue templates. That means that in the following template:
<template>
<div>
<b-button variant="primary" @click="play">PLAY</b-button>
</div>
</template>
the b-button is a child component, which is stubbed when using shallowMount
in the unit tests for our component. That's the reason why we can't find an element button:
const wrapper = shallowMount(MyComponent);
wrapper.find('button'); // won't work
We can find the child component like this:
wrapper.find(BButton); // BButton has to be imported from bootstrap-vue
but if we try to output the rendered element:
console.log(wrapper.find(BButton).element);
we'll get:
HTMLUnknownElement {}
The BButton
as a child component hasn't been fully rendered and there is no button
element in the DOM tree. But when we use mount
we have this behaviour:
const wrapper = mount(MyComponent);
console.log(wrapper.find(BButton).element);
we'll get:
HTMLButtonElement { _prevClass: 'btn btn-primary' }
We see that mount
has rendered the child component. When we use mount
we can directly access the button
element:
wrapper.find('button');
Now that we have the button
we can trigger an event like click
on it.
I hope this helps other beginners too. The examples are very simplified, don't forget to create localVue
using createLocalVue
and pass it to the mount
method as illustrated in the question.
When using BootstrapVue think very carefully which mounting method you need.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With