DEV Community

Reynold Osei Adade
Reynold Osei Adade

Posted on

Why you need to unit-test results and not implementation

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>
Enter fullscreen mode Exit fullscreen mode
// HelloWorld.vue
export default {
  name: "Counter",
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    increment() {
      this.count++;
    },
    decrement() {
      this.count--;
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

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);
  });
});
Enter fullscreen mode Exit fullscreen mode

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 incrementand decrement functions work correctly

  • The 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");
  });
Enter fullscreen mode Exit fullscreen mode

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

Customize configuration

See Configuration Reference.

Top comments (0)