Unit tests are one of the best ways to check for code quality. Irrespective of which part of the stack you find yourself on the principle of unit tests will not change, and that is to test for the output of your code or your unit of code. For backend developers it may be a results of the functions that they write but for frontend developers developers our output is what is presented on the DOM and not the internals of a code. This is because we are responsible for what a user sees when they use our applications and that is what our unit tests should also cover
Let check out a simple counting app
<!--HelloWorld.vue -->
<template>
<div class="hello">
<div>
{{ count }}
</div>
<button @click="decrement" data-testid="decreaseButton">Decrease</button>
<button @click="increment" data-testid="increaseButton">Increase</button>
</div>
</template>
// HelloWorld.vue
export default {
name: "Counter",
data() {
return {
count: 0,
};
},
methods: {
increment() {
this.count++;
},
decrement() {
this.count--;
},
},
};
This application just increases or decreases the count
Unit test principles for frontend devs
- Always test for outputs
- Changing implementation should not fail tests if output has not changed
- Your unit tests should follow closely to the users experience
Any test that fails because the implementation has changed is generally not a proper unit test, that sounds absurd, I understand, I thought so too but let me give you an example
//HelloWorld.spec.js
describe("HelloWorld.vue", () => {
test("increment should increase count", () => {
const wrapper = mount(HelloWorld);
wrapper.vm.increment();
expect(wrapper.vm.count).toBe(1);
});
test("decrement should decrease count", () => {
const wrapper = mount(HelloWorld);
wrapper.vm.decrement();
expect(wrapper.vm.count).toBe(-1);
});
});
The test above is a representation of 90% of the tutorials or example we will see on most dev sites. If we are being truthful that is how most of us would test this but we are forgetting that our components just don't have methods there are buttons and a whole DOM to consider, as frontend devs our output is what the user sees not what goes on in our code so that is what we need to test for.
What this test does
Our implementation of the
increment
anddecrement
functions work correctlyThe test will fail if function names are changed
A better test
As frontend developers we should always ensure we write unit tests that test for the output of our code rather that how the output is generated.
test("increment button should increase count", async () => {
const wrapper = mount(HelloWorld);
const button = wrapper.find("[data-testid='incrementButton']");
button.trigger("click");
await wrapper.vm.$nextTick();
expect(wrapper.find("[data-testid='countValue']").text()).toBe("1");
// expect(wrapper.vm.count).toBe(1);
});
test("decrement button should decrease count", async () => {
const wrapper = mount(HelloWorld);
const button = wrapper.find("[data-testid='decrementButton']");
button.trigger("click");
await wrapper.vm.$nextTick();
expect(wrapper.find("[data-testid='countValue']").text()).toBe("-1");
});
What this test does
- Button clicks work correctly
- Changes are presented correctly in our DOM
- It proves to us that our implementation of the increment and decrement functions are correct
- If you should change the names of the increment and decrement methods it will not break your test since it did not break your code
- If your should swap the methods of the increment and decrement buttons your test will fail ( try changing the name of your methods)
You can read more on this here
You can check out the repo here
properunittests
Project setup
npm install
Compiles and hot-reloads for development
npm run serve
Compiles and minifies for production
npm run build
Lints and fixes files
npm run lint
Top comments (0)