7

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?

7
  • To get your console.log running, you could try this: wrapper.vm.play() Commented Mar 5, 2020 at 9:31
  • @lucas Thanks, but my goal is not to get the console.log running by explicitly calling the method. I want to test that triggering the click event will call the method. Commented Mar 5, 2020 at 9:41
  • got it. But does it work? Another option is trying to change this to wrapper.find('b-button').trigger('click') instead Commented Mar 5, 2020 at 9:51
  • @lucas No, wrapper.find('b-button') won't work. Neither wrapper.find('button') will. You have to pass the component name to the find method of wrapper when using BoostrapVue. I don't how to proceed further in order to trigger an event. Commented Mar 5, 2020 at 9:54
  • 1
    I'm not familiar with BootstrapVue, but I have an idea that might help you. Check how they test the buttons on their repository. It hopefully might give you some insight. github.com/bootstrap-vue/bootstrap-vue/blob/dev/src/components/… Commented Mar 5, 2020 at 9:58

2 Answers 2

14

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:

  • mount
  • shallowMount

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.

Sign up to request clarification or add additional context in comments.

2 Comments

And also applicable to any other component library (or even your own child components), not just BootstrapVue
@TroyMorehouse Yes, you're absolutely right. Here we set an example on BootstrapVue, which I believe is a popular library.
4

While still performing a shallowMount you should be able to do this:

wrapper.find(BButton).vm.$listeners.click();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.