Skip to content
Advertisement

Vue.js – Unit testing component with complex children

Let’s assume a basic Bootstrap driven HTML form as part of a custom Vue component MyForm.vue

<template>
  <form>
    <div class="form-group">
      <label for="email">Email address</label>
      <input type="email" class="form-control" id="email">
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
  </form>
</template>

A unit test for testing if the template is rendered successfully is pretty simple

describe('MyForm', () => {

  let wrapper;

  beforeEach(...);

  it('Should be rendered', () => {

    let field = wrapper.find('#email');
    expect(field.element.value).toEqual('');
  });
});

This line works field.element.value works because field.element is of native type HtmlInputElement with value attribute.

What if I want access an attribute of a complex component let’s say of b-form-input, the Bootstrap-Vue’s default input element? b-form-input is of type BFormInput how to deal with it? Just cast the HtmlElement to BFormInput?

<template>
  <b-form>
    <b-form-group label="Email">
      <b-form-input type="email" id="email"></b-form-input>
    </b-form-group>
    <b-button type="submit" variant="primary">Submit</button>
  </b-form>
</template>

How to test non-native components? Specially with type-safety means TypeScript. Any ideas?

Edit 03/01/2020

Following up to muka.gergely‘s answer I found this article. shallowMount is stubbing all child components by default which prevents also event handling. Moreover shallowMount allows to manually unstub components, in my case to unstub b-form and b-button for submit event testing.

const stubs = { // Originally intended to provide custom stubs, here used to unstub components
  BButton,
  BForm,
};

wrapper = shallowMount<MyComponent>(MyComponent, {stubs});

This effects that these components are rendered instead of stubbed. All remaining components like the b-form-input are still automatically stubbed.

Advertisement

Answer

You have to mount the elements before testing them. You don’t test the Vue component that you wrote, but the rendered output.

You should add vue-test-utils then your unit testing library (Jest and Mocha are well supported).

Here’s a basic unit test for App.vue (with Vuetify and vue-router):

import Vue from 'vue'
import { shallowMount, createLocalVue } from '@vue/test-utils'
import Vuetify from 'vuetify'
import VueRouter from 'vue-router'
import App from '@/App';

Vue.use(Vuetify)

const localVue = createLocalVue()
localVue.use(VueRouter)
const router = new VueRouter()

describe('App.vue', () => {
    let vuetify

    beforeEach(() => {
        vuetify = new Vuetify()
    })

    it('mounts and renders', () => {
        const wrapper = shallowMount(App, { localVue, vuetify, router });

        expect(wrapper.html()).toBeTruthy();
    });
});

You can see that I used shallowMount() as I wasn’t interested in testing the children of App.vue (they all have their separate unit tests). If I had been, then I should’ve used mount() instead.

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement