Web-Design
Tuesday December 22, 2020 By David Quintanilla
Testing Vue Applications With The Vue Testing Library — Smashing Magazine


About The Writer

Kelvin is an unbiased software program maker at present constructing Sailscasts — a platform to be taught server-side JavaScript. He’s additionally a technical author and …
More about
Kelvin

The Vue Testing library can assist you to check your functions by mirroring the way in which {that a} consumer would work together with them. Right here’s every thing you might want to know if you wish to get began straight away.

On this article, we’ll take a look at testing Vue functions utilizing the Vue Testing Library — a light-weight library that emphasizes testing your front-end software from the consumer’s perspective.

The next assumptions are made all through this text:

  • The reader is conversant in Vue.
  • The reader is conversant in testing software UI.

Conventionally, in Vue userland, whenever you need to check your software, you attain out for @vue/test-utils — the official testing library for Vue. @vue/test-utils offers APIs to check situations of rendered Vue elements. Like so:

// instance.spec.js
import { shallowMount } from '@vue/test-utils'
import HelloWorld from '@/elements/HelloWorld.vue'

describe('HelloWorld.vue', () => {
  it('renders props.msg when handed', () => {
    const msg = 'new message'
    const wrapper = shallowMount(HelloWorld, {
      propsData: { msg }
    })
    count on(wrapper.textual content()).toMatch(msg)
  })
})

You possibly can see we’re mounting an occasion of the Vue part utilizing the shallowMount operate offered by @vue/test-utils.

The issue with the above method to testing Vue functions is that the end-user will probably be interacting with the DOM and has no data of how Vue renders the UI. As an alternative, he/she will probably be discovering UI components by textual content content material, the label of the enter component, and another visible cues on the web page.

A greater method will probably be writing assessments in your Vue functions in such a means that mirrors how an precise consumer will work together with it e.g searching for a button to increment the amount of a product in a checkout web page, therefore Vue Testing Library.

What Is Vue Testing Library?

Vue Testing Library is a light-weight testing library for Vue that gives light-weight utility features on prime of @vue/test-utils. It was created with a easy tenet:

The extra your assessments resemble the way in which your software program is used, the extra confidence they can provide you.
testing-library.com

Why Use Vue Testing Library

  • You need to write assessments that aren’t centered on implementation particulars i.e testing how the answer is applied quite than if it produces the specified output.

  • You need to write assessments that concentrate on the precise DOM nodes and never rendered Vue elements.

  • You need to write assessments that question the DOM in the identical means a consumer would.

How Vue Testing Library Works

Vue Testing Library features by offering utilities for querying the DOM in the identical means a consumer would work together with the DOM. These utilities let you discover components by their label textual content, discover hyperlinks and buttons from their textual content content material and assert that your Vue software is totally accessible.

For circumstances the place it doesn’t make sense or just isn’t sensible to seek out components by their textual content content material or label, Vue testing Library offers a beneficial strategy to discover these components through the use of data-testid attribute as an escape hatch for locating these components.

The data-testid attribute is added to the HTML component you intend on querying for in your check. E.g

<button data-testid="checkoutButton">Test Out</button>

Getting Began With Vue Testing Library

Now that you’ve seen why you need to use Vue Testing Library and the way it works, let’s proceed by setting it up in a model new Vue CLI generated Vue venture.

First, we’ll generate a brand new Vue software by operating the beneath command within the terminal (assuming you may have Vue CLI put in in your machine):

vue create vue-testing-library-demo

To run our assessments, we will probably be utilizing Jest — a check runner developed by Fb. Vue CLI has a plugin that simply units up Jest. Let’s add that plugin:

vue add unit-jest

You’ll discover the plugin added a brand new script in package deal.json:

 "check:unit": "vue-cli-service check:unit",

This could be used to run the assessments. It additionally added a brand new assessments folder in src and contained in the assessments folder a unit folder with an instance check file known as instance.spec.js. Based mostly on the configuration of Jest, after we run npm run check:unit Jest will search for information in assessments listing and run the check information. Let’s run the instance check file:

npm run check:unit

This could run the instance.spec.js check file in assessments/unit listing. Let’s take a look at the content material of this file:

import { shallowMount } from '@vue/test-utils'
import HelloWorld from '@/elements/HelloWorld.vue'

describe('HelloWorld.vue', () => {
  it('renders props.msg when handed', () => {
    const msg = 'new message'
    const wrapper = shallowMount(HelloWorld, {
      propsData: { msg }
    })
    count on(wrapper.textual content()).toMatch(msg)
  })
})

By default, putting in Jest with the Vue CLI plugin will set up @vue/test-utils, therefore the above check file is utilizing the shallowMount operate from @vue/test-utils. A fast strategy to get conversant in Vue Testing Library is to shortly modify this similar check file to make use of Vue Testing Library as an alternative of @vue/test-utils.

We might do that by first uninstalling @vue/test-utils as we received’t be needing it.

npm uninstall @vue/test-utils --save-dev

Then we set up Vue Testing Library as a improvement dependency:

npm set up @testing-library/vue --save-dev

Then we proceed to change assessments/unit/instance.spec.js to this:

import { render } from '@testing-library/vue'
import HelloWorld from '@/elements/HelloWorld.vue'

describe('HelloWorld.vue', () => {
  it('renders props.msg when handed', () => {
    const msg = 'new message'
    const { getByText } = render(HelloWorld, {
      props: { msg }
    })
    getByText(msg)
  })
})

Run the check once more and it ought to nonetheless go. Let’s take a look at what we did:

  • We use the render operate uncovered by Vue Testing Library to render the HelloWorld elements. render is the one means of rendering elements in Vue Testing Library. Once you name render, you go within the Vue part and an non-compulsory choices object.

  • We then use the choices object to go within the msg props wanted by the HelloWorld part. render will return an object with helper strategies to question the DOM and a type of strategies is getByText.

  • We then use getByText to claim if a component with the textual content content material of ‘new message’ exist within the DOM.

By now you may need observed the shift from fascinated by testing the rendered Vue part to what the consumer sees within the DOM. This shift will permit you check your functions from the consumer perspective versus focusing extra on the implementation particulars.

Our Demo App

Now that now we have established how testing is finished in Vue utilizing Vue Testing Library, we’ll proceed to check our demo software. However first, we’ll flesh out the UI for the app. Our demo app is an easy checkout web page for a product. We will probably be testing if the consumer can increment the amount of the product earlier than checkout, he/she will see the product title and worth, and so forth. Let’s get began.

First, create a brand new Vue part known as checkout in elements/ listing and add the snippet beneath to it:

<template>
    <div class="checkout">
        <h1>{{ product.title }} - <span data-testid="finalPrice">${{ product.worth }}</span></h1>
        <div class="quantity-wrapper">
            <div>
                <label for="quanity">Amount</label>
                <enter sort="quantity" v-model="amount" title="amount" class="quantity-input" />
            </div>
           <div>
                <button @click on="incrementQuantity" class="quantity-btn">+</button>
                <button @click on="decrementQuantity" class="quantity-btn">-</button>
           </div>
        </div>
          <p>closing worth - $<span data-testId="finalPrice">{{ finalPrice }}</span></p>
        <button @click on="checkout" class="checkout-btn">Checkout</button>
    </div>
</template>
<script>
export default {
    information() {
        return {
            amount: 1,
        }
    },
    props: {
    product: {
        required: true
        }
    },
    computed: {
        finalPrice() {
            return this.product.worth * this.amount
        }
    },
    strategies: {
        incrementQuantity() {
            this.amount++;
        },
        decrementQuantity() {
            if (this.amount == 1) return;
            this.quantity--;
        },
        checkout() {

        }
    }
}
</script>

<fashion scoped>
.quantity-wrapper {
    margin: 2em auto;
    width: 50%;
    show: flex;
    justify-content: heart;
}

.quantity-wrapper div {
    margin-right: 2em;
}
.quantity-input {
    margin-left: 0.5em;
}
.quantity-wrapper button {
    margin-right: 1em;
}
button {
    cursor: pointer;
}
</fashion>

Then modify App.vue to:

<template>
  <div id="app">
    <check-out :product="product" />
  </div>
</template>

<script>
import CheckOut from './elements/CheckOut.vue'

export default {
  title: 'App',
  information() {
     return {
          product: {
          title: 'Shure Mic SM7B',
          worth: 200,
      }
    }
  },
  elements: {
    CheckOut
  }
}
</script>

<fashion>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: heart;
  coloration: #2c3e50;
  margin-top: 60px;
}
</fashion>

For our check case we will probably be testing the next situations:

  1. Can the consumer see the product title?
  2. Can the consumer see the product worth?
  3. Can the consumer increment product amount?
  4. Can the consumer decrement product amount?
  5. Can the consumer see the up to date complete worth in real-time as the amount modifications?

Our UI is fairly minimalistic because the emphasis is on testing with Vue Testing Library. Let’s proceed to check the Checkout part. Create a brand new check file in assessments/unit/ known as checkout.spec.js.

We’ll then proceed to scaffold the check file:

import { render, fireEvent } from '@testing-library/vue'
import CheckOut from '@/elements/CheckOut.vue'

const product = {
    title: 'Korg Kronos',
    worth: 1200
}
describe('Checkout.vue', () => {
  // assessments goes right here
})

Our very first check case will probably be to test if the product title is rendered. We might achieve this like so:

 it('renders product title', () => {
        const { getByText } = render(CheckOut, {
            props: { product }
        })

        getByText(product.title)
 })

Then we’ll test if the product worth is rendered:

it('renders product worth', () => {
        const { getByText } = render(CheckOut, {
            props: { product }
        })

        getByText("$" + product.worth)
 })

Going ahead with testing the Checkout part, we’ll check if the preliminary amount the consumer sees is 1 utilizing the getByDisplayValue helper technique:

it('renders preliminary amount as 1', () => {
        const { getByDisplayValue, getByText } = render(CheckOut, {
            props: { product }
        })
        getByDisplayValue(1)
    })

Subsequent up, we will probably be checking if when the consumer clicks the button to increment product amount, the amount is incremented. We’ll do that by firing the clicking occasion utilizing the fireEvent utility from Vue Testing Library. Right here is the entire implementation:

it('increments product amount', async () => {
        const { getByDisplayValue, getByText } = render(CheckOut, {
            props: { product }
        })
        const incrementQuantityButton = getByText('+')
        await fireEvent.click on(incrementQuantityButton)
        getByDisplayValue(2)
})

We’ll do the identical for decrement when the amount is 1 — on this case, we don’t decrement the amount. And in addition when the amount is 2. Let’s write each check circumstances.

it('doesn't decrement amount when quanty is 1', async () => {
        const { getByDisplayValue, getByText } = render(CheckOut, {
            props: { product }
        })
        const decrementQuantityButton = getByText('-')
        await fireEvent.click on(decrementQuantityButton)
        getByDisplayValue(1)
    })

 it('decrement amount when amount higher than 1', async () => {
        const { getByDisplayValue, getByText } = render(CheckOut, {
            props: { product }
        })
        const incrementQuantityButton = getByText('+')
        const decrementQuantityButton = getByText('-')
        await fireEvent.click on(incrementQuantityButton)
        await fireEvent.click on(decrementQuantityButton)
        getByDisplayValue(1)
    })

Lastly, we’ll check if the ultimate worth is being calculated accordingly and exhibited to the consumer when each the increment and decrement amount buttons are clicked.

it('shows appropriate closing worth when increment button is clicked', async () => {
        const {  getByText, getByTestId } = render(CheckOut, {
            props: { product }
        })
        const incrementQuantityButton = getByText('+')
        await fireEvent.click on(incrementQuantityButton)
        getByText(product.worth * 2)
    })

it('shows appropriate closing worth when decrement button is clicked', async () => {
        const {  getByText} = render(CheckOut, {
            props: { product }
        })
        const incrementQuantityButton = getByText('+')
        const decrementQuantityButton = getByText('-')
        await fireEvent.click on(incrementQuantityButton)
        await fireEvent.click on(decrementQuantityButton)
        getByText(product.worth)
    })

All all through our check circumstances, you’ll discover that we had been extra centered on writing our assessments from the attitude of what the consumer will see and work together with. Writing assessments this fashion ensures that we’re testing what issues to the customers of the appliance.

Conclusion

This text introduces another library and method for testing Vue functions known as Vue Testing Library, we see tips on how to set it up and write assessments for Vue elements with it.

Assets

You will discover the demo project on GitHub.

Smashing Editorial
(ra, yk, il)



Source link