Image

unit-testing-vue-3

6 min read
Last update: December 19, 2021

Testing Props & User Interaction

In the previous lesson, we learned how to write and run our first unit tests. In this lesson, we’ll continue writing simple tests for a component that requires user interaction and takes in some props.


Random Number Component

In lesson 1, we identified the inputs and outputs of a component that generates a random number within the range of its min and max props.

Here is the code for that component:

📃src/components/RandomNumber.vue

<template>
    <div>
        <span>{{ randomNumber }}</span>
        <button @click="getRandomNumber">Generate Random Number</button>
    </div>
</template>

<script>
    export default {
        props: {
            min: {
                type: Number,
                default: 1
            },
            max: {
                type: Number,
                default: 10
            }
        },
        data() {
            return {
                randomNumber: 0
            }
        },
        methods: {
            getRandomNumber() {
                this.randomNumber = Math.floor(Math.random() * (this.max - this.min + 1)) + this.min;
            }
        }
    }
</script>

What tests should we write?

In terms of this component’s inputs, it’s pretty obvious that the props are inputs since props are literally fed into the component. Another input would be the user interaction, whether a user clicked the button or not, which runs the method that generates the random number. The output is the rendered HTML displaying the randomNumber.

Inputs

Props:

  • min & max

User Interaction:

  • Clicking of the Generate Random Number button

Outputs

Rendered Output (DOM)

  • Is the number displayed on the screen between min and max?

We can use this knowledge to figure out what to test in this component:

  1. By default, the randomNumber data value should be 0
  2. If we click the generate button, randomNumber should be between 1 (min) and 10 (max)
  3. If we change the min and max props to 200 and 300 and click the button, randomNumber should be between 200 (min) and 300 (max).

Random Number Tests

To get started testing this component, we’ll created a new test file: /tests/unit/RandomNumber.spec.js

For now, we’ll simply scaffold out the tests and write default assertions that we know will fail. In the last lesson, we looked at scaffolding our tests with an assertion we know would pass. By using an assertion we know will definitely fail, this serves a similar purpose of making sure our components are behaving how we expect them to at first. Then, we’ll do the work to make the tests pass.

📃**/tests/unit/RandomNumber.spec.js**

import { mount } from '@vue/test-utils'
import RandomNumber from '@/components/RandomNumber'

describe('RandomNumber', () => {
test('By default, randomNumber data value should be 0', () => {
expect(true).toBe(false);
})

test('If button is clicked, randomNumber should be between 1 and 10', () => {
expect(true).toBe(false);
})

test('If button is clicked, randomNumber should be between 200 and 300', () => {
expect(true).toBe(false);
})
})

If we run npm run test:unit, we’ll see 3 failures for this test.


Checking the default random number

Looking at the component, we know that the default value for randomNumber is 0, so why would you even test for this? Well, what if someone else on our team changed the default randomNumber? Testing it gives us confidence 0 will always be displayed when the component is first loaded.

Our first step to test this is to mount the component we are testing (RandomNumber.vue), which gives us the wrapper that lets us dive into the component and test what we need to. The test is going to look like this:

📃 /tests/unit/RandomNumber.spec.js

test('By default, randomNumber data value should be 0', () => {
const wrapper = mount(RandomNumber)
expect(wrapper.html()).toContain('<span>0</span>')
})

Here, we’re using the wrapper to get the html of our RandomNumber component, and asserting that we expect that html toContain a span with a 0 in its inner HTML.

If we run this test by typing npm run test:unit in our terminal, we’ll see that now only 2 tests are failing, and we’ve gotten our first test to pass. Now we can move on to our next test, which requires some user interaction.


Simulating User Interaction

We need to verify that when we click the generate random number button, we get a random number between the min and max props. The defaults for min and max are 1 and 10 respectively, so the random number should fall in that range.

Just as we have before, we’ll need to mount the RandomNumber component. The new concept here is that we need to trigger a button click on our generate random number button (which runs the method that uses the min and max props to generate a new randomNumber).

We’ll get a reference to the button element using the find() method, and then use the trigger() method to trigger an event on the wrapper DOM node. The first and required argument to the trigger method is a string for what event type to trigger. In this case, we want to trigger a 'click' event on the button.

📃 tests/unit/RandomNumber.spec.js

test('If button is clicked, randomNumber should be between 1 and 10', () => {
const wrapper = mount(RandomNumber)
await wrapper.find('button').trigger('click')
})

Now that the button was ‘clicked’, it should have generated a random number. In order to access that number within the rendered html, we can write:

const randomNumber = parseInt(wrapper.find('span').text())

This effectively finds the span, and accesses that element’s text content. But because we need that content to be an integer, we’ve wrapped in in parseInt.

Finally, we can use some Jest assertions to make sure the random number falls between the min prop 1 and max prop 10.

📃 tests/unit/RandomNumber.spec.js

test('If button is clicked, randomNumber should be between 1 and 10', async () => {
const wrapper = mount(RandomNumber)
await wrapper.find('button').trigger('click')

const randomNumber = parseInt(wrapper.find('span').text())
})

If we run the tests now, we should see 2 passing. Now we can work on the final test.


Setting different prop values

Since someone using this component can change the min and max value range via the min and max props, we need to test for this. To do this, we’ll use the mount() method, which takes an optional second argument, where we can pass in some options, including propsData. This can be used to reset the min and max values, to 200 and 300 respectively, in our case.

📃 /tests/unit/RandomNumber.spec.js

test('If button is clicked, randomNumber should be between 200 and 300', async () => {
const wrapper = mount(RandomNumber, {
props: {
min: 200,
max: 300
}
})
})

With our new min and max, the test is going to look pretty similar to the one we just wrote above.

📃 /tests/unit/RandomNumber.spec.js

test('If button is clicked, randomNumber should be between 200 and 300', async () => {
const wrapper = mount(RandomNumber, {
props: {
min: 200,
max: 300
}
})

await wrapper.find('button').trigger('click')
const randomNumber = parseInt(wrapper.find('span').text())

expect(randomNumber).toBeGreaterThanOrEqual(200)
expect(randomNumber).toBeLessThanOrEqual(300)
})

At this point you can run your tests and all of them should pass. Congratulations!


Let’s Revue

Not all components are created equally, so testing them often means we’ll have to consider things like simulating button clicks, and testing props. In our next lesson, we’ll continue learning about testing common aspects of Vue components.