> For the complete documentation index, see [llms.txt](https://devworkshops.gitbook.io/masteringvuejs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://devworkshops.gitbook.io/masteringvuejs/testing/unit-testing.md).

# Unit Testing

If you were brave enough to run `npm run test:unit` in the previous section, that's what you were expecting to see:

![By default Jest automatically creates a sample test and it does pass :)](/files/-LW4nThdqoU0_b2tz1zv)

## Creating test case

What about practicing a bit of TDD (test-driven development)? Let's create a new test spec file called **counter.spec.js** which is going to be the test cases for a component that still doesn't exist.

We basically want to test a counter component which counts the number of times the user clicks on the button, if the user clicks with the right button it should then decrease the value. We also want different CSS classes for when the count is positive and negative.

The spec below will test all those things, but it's not that readable. There's a lot of room for improvement.

{% code title="counter.spec.js" %}

```javascript
import { shallowMount } from '@vue/test-utils'
// this component still doesn't exist, so expect an error here
import Counter from '@/components/Counter.vue'

describe('Counter.vue', () => {
    it('increase when click, decrease when right-click', () => {
        const wrapper = shallowMount(Counter)
        expect(wrapper.vm.counter).toBe(0)
        wrapper.find('button').trigger('click')
        expect(wrapper.vm.counter).toBe(1)
        expect(wrapper.find('button').classes('positive')).toBeTruthy()
        wrapper.find('button').trigger('contextmenu')
        wrapper.find('button').trigger('contextmenu')
        expect(wrapper.vm.counter).toBe(-1)
        expect(wrapper.find('button').classes('negative')).toBeTruthy()
    })
})
```

{% endcode %}

Let's try to separate at least the left and right clicks. The code now is easier to follow, especially because we're splitting the different features of the component into different test cases. But there's still a lot of repetition.

{% code title="counter.spec.js" %}

```javascript
import { shallowMount } from '@vue/test-utils'
import Counter from '@/components/Counter.vue'

describe('Counter.vue', () => {
    it('increase when click', () => {
        const wrapper = shallowMount(Counter)
        expect(wrapper.vm.counter).toBe(0)
        wrapper.find('button').trigger('click')
        expect(wrapper.vm.counter).toBe(1)
        expect(wrapper.find('button').classes('positive')).toBeTruthy()
    })
    it('decrease when right-click', () => {
        const wrapper = shallowMount(Counter)
        expect(wrapper.vm.counter).toBe(0)
        wrapper.find('button').trigger('contextmenu')
        expect(wrapper.vm.counter).toBe(-1)
        expect(wrapper.find('button').classes('negative')).toBeTruthy()
    })
})
```

{% endcode %}

Why not reusing some of the code for every test case? We're moving the mounting the initial value of the counter to a common place. Now all test cases are very slim.

{% code title="counter.spec.js" %}

```javascript
import { shallowMount } from "@vue/test-utils";
import Counter from "@/components/Counter.vue";

describe("Counter.vue", () => {
  let wrapper;

  // this will before every test case
  beforeEach(() => {
    wrapper = shallowMount(Counter);
    expect(wrapper.vm.counter).toBe(0);
  });

  it("increase when click", () => {
    wrapper.find("button").trigger("click");
    expect(wrapper.vm.counter).toBe(1);
    expect(wrapper.find("button").classes("positive")).toBeTruthy();
  });

  it("decrease when right-click", () => {
    wrapper.find("button").trigger("contextmenu");
    expect(wrapper.vm.counter).toBe(-1);
    expect(wrapper.find("button").classes("negative")).toBeTruthy();
  });
});
```

{% endcode %}

Try running the tests now with `npm run test:unit`, you should get a bunch of errors, but as you implement your components, the errors will go away.

## Implementing component

Let's now create the component to meet all the requirements from the unit test. We're going to create a **Counter.vue** under the components folder.

{% code title="Counter.vue" %}

```markup
<template>
  <div>
    <button
      @click="increase()"
      @contextmenu.prevent="decrease()"
      :class="{'positive':counter>0,'negative':counter<0} "
    >{{counter}}</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      counter: 0
    };
  },
  methods: {
    increase() {
      this.counter++;
    },
    decrease() {
      this.counter--;
    }
  }
};
</script>

<style scoped>
.positive {
  background-color: blue;
}
.negative {
  background-color: red;
}
</style>
```

{% endcode %}

Now meeting all the requirements and running the unit tests again, we'll have all greens.

![](/files/-LW6qdIHm0I4Iotk8JoV)

## Creating a unit test for an existing component

Early on we created a reusable component called **InvalidFeedback.vue** to show error messages when whenever there's a validation error. Let's create a quick unit test for it. First we create a new file under tests/unit called **invalid-feedback.spec.js** and add the content below.

```javascript
import { shallowMount } from '@vue/test-utils'
import InvalidFeedback from '@/components/InvalidFeedback.vue'

describe('InvalidFeedback.vue', () => {
    it('Failed IsUnique shows message', () => {
        const wrapper = shallowMount(InvalidFeedback, {
            propsData: {
                model: {
                    isUnique: false
                }
            }
        })
        expect(wrapper.text()).toMatch('already exists')
    })
    it('Failed MinValue shows message', () => {
        const wrapper = shallowMount(InvalidFeedback, {
            propsData: {
                field: 'Name',
                model: {
                    minValue: false,
                    $params: { minValue: { min: 10 } }
                }
            }
        })
        expect(wrapper.text()).toMatch('Name must be equal or greater than 10.')
    })
})
```

This is a very simple test, it's basically only testing for the `IsUnique` and  `MinValue` exceptions. We should definitely go and add all exception handling here but for now that will suffice.&#x20;


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://devworkshops.gitbook.io/masteringvuejs/testing/unit-testing.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
