Constantin Müller
Constantin Müller

Reputation: 1300

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.

Upvotes: 4

Views: 1662

Answers (1)

muka.gergely
muka.gergely

Reputation: 8329

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.

Upvotes: 2

Related Questions