Unit vs E2E Testing for Vue.js

Unit vs E2E Testing for Vue.js

In this article, you'll see Unit vs E2E Testing for Vue.js. What's the difference? Do you need both?

In this article, you'll see Unit vs E2E Testing for Vue.js. What's the difference? Do you need both?

Writing tests when developing Vue.js apps can save you a lot of time which would be otherwise spent fixing bugs. The bigger and more complex your app gets, the truer this becomes.

There are two types of tests that are commonly performed for web applications: unit tests and end-to-end (E2E) tests.

What’s the difference? Do you need both?

Let’s explore.

Unit tests

The idea of a “unit” in testing, is to break down the code into small, easily testable parts. Usually, the unit is a single function, but can also be a class or even a complex algorithm.

A crucial concept of unit testing is that a given input of the function should always result in the same output.

For example, if we had a function that added two numbers called add we could write a unit test to ensure that a particular pair of numbers we provided as arguments would always return the output we expect.

add.spec.js

// Function we want to test
const add = (x, y) => x + y;

// Unit test
test("should add two numbers", () => {
  const result = add(2, 3);
  expect(result).toBe(5);
});


Any time we run that test and it doesn’t equal 5, we can conclude a bug has entered our code.

Component tests

In most Vue.js applications functions don’t really represent the atomic makeup of the app. Sure, we can unit test our methods, but what we also really care about is the HTML that’s generated.

For this reason, the unit in a Vue.js app test is a component rather than a function.

How do we test components? Let’s take this one as an example:

displayGreeting.js

export default {
  template: `<div>Hello, {{ name }}</div>`,
  props: ['name']
};


As previously stated, a unit test must, for a given input (in this case, a prop), return a consistent output (in this case, text content).

Using a library like Vue Test Utils, we can mount a Vue component in memory and create a “wrapper” object. We can then query the wrapper to make assertions about the rendered HTML.

displayGreeting.spec.js

import displayGreeting from "./displayGreeting.js";

test("displays message", () => {
  const name = "Michael";
  const wrapper = mount(displayGreeting, { propsData: { name } });
  expect(wrapper.text()).toBe(`Hello, ${name}`);
});


Snapshot tests

In the above example, we used the wrapper.text() to query for the text in the component output.

In most components, though, testing the veracity of the output will require more than one snippet of text. We often want to ensure that a variety of elements are present.

Maybe it’d be easier to test the entire HTML output of the component?

Another kind of component unit test is a snapshot test where you do exactly that.

How it works is that you generate the output of the component once and write it to a text file. For example:

displayGreeting.spec.js.snap

exports[`renders correctly 1`] = `<div>Hello, Michael</div>`;


Now, any time the tests run, if the rendered output of the component differs from what’s in the file, the test will fail.

Snapshots are a blunt instrument, but they are good for testing components which display a lot of HTML.

E2E tests

E2E (end-to-end) testing is a type of functional test. Unlike a unit test, you’re not breaking the application down into smaller parts in order to test it - you’re testing the entire application.

E2E tests interact with your app just like a real user would. For example, you may write an E2E test which:

  1. Loads your site
  2. Clicks on the “Sign up” link
  3. Provides some valid details to the inputs in the registration form
  4. Click the “Register button”.

This test should pass if an authentication token has been stored in the cookies and the app redirected to the profile page.

Tools

E2E tests are made on top of a browser automation driver like Selenium that provides an API to drive the browser.

An E2E testing framework like Cypress or Nightwatch will then provide a way for you to script your E2E tests for the browser automation driver.

The following code is what you might use in Nightwatch to perform the test described in the section above. You can probably tell what it does even if you’ve never used Nightwatch.

register.spec.js

"register user": browser => {

  // Navigate to register page
  browser.page.register()
    .navigate()
    .waitForElementPresent(form.selector, 5000);

  // Fill out the form
  register.section.form
    .setValue("@nameInput", "Anthony")
    .setValue("@emailInput", "[email protected]")
    .setValue("@passwordInput", "test1234")
    .click("@submitButton");

  // Make assertions
  browser
    .assert.urlEquals(profile.url)
    .getCookie(name, (result) => {
      this.assert.equals(result.name, 'auth');
    }
  });

}


Unit and E2E comparison

Unit pros:

  • Tests run fast
  • Test are precise and allow you to identify exact problems

Unit cons:

  • Tests run fast
  • Test are precise and allow you to identify exact problems

E2E pros:

  • Tests run fast
  • Test are precise and allow you to identify exact problems

E2E cons:

  • Tests run fast
  • Test are precise and allow you to identify exact problems

Verdict

In my opinion, a combination of both unit and E2E tests is the best approach. The cons of one type can be mostly nullified by the pros of the other.

For example, E2E test won’t tell you the root cause of failure, but unit tests will, while unit tests won’t tell you if the whole application is working or not, while E2E tests will.

Using these test types together will give you a lot of confidence in your application, allowing you to add features or refactor without fear of collapse.

The general strategy for combining unit and E2E tests for a Vue.js app is this:

  • Tests run fast
  • Test are precise and allow you to identify exact problems
Bonus: testing tools

So you’re ready to start testing, what tool can you use?

For frontend unit testing, the best tool right now is Jest. It has many useful features, for example, allowing you to compile TypeScript and modern JS before the tests run.

You can use Jest in conjunction with Vue Test Utils which allows you to mount and query Vue components.

For E2E, the state-of-the-art tool right now is Cypress. Another more basic tool that also works well is Nightwatch.

The good news is that it’s easy to add all of these tools to a Vue application with Vue CLI 3.

Finally, it’s a good idea to use a continuous integration tool like Travis or Circle CI that will run your tests in the cloud (especially good for time-consuming E2E tests) and deploy your code conditional on all your tests passing.

Happy testing!

Vue.js Testing Guide: Unit testing in Vue and Mocha

Vue.js Testing Guide: Unit testing in Vue and Mocha

Testing in Vue is really simple! In this post, you'll learn a basic unit test using Mocha and Vue.js

This document aims to outline some short examples of how to unit test using a Vue project. This guide is specifically designed to be used on the following Vue set up.

What project setup? What packages are needed?

With the above setup, you can now start writing Mocha unit tests in Vue. The steps below show how to create tests.

Creating test files

Files that are picked for the testing end with **_.spec.js_**. You can see how this is set or change this inside **_package.json_**.

package.json

"scripts": {
    "test:unit": "vue-cli-service test:unit src/**/*.spec.js"
  }
How to run tests?

In the terminal type:

npm run test:unit

Let's see how a basic unit test file will look…

import Spinner from "@/ui/Spinner";
import AppLoadingScreen from "./AppLoadingScreen";
import { shallowMount } from "@vue/test-utils";
import { expect } from "chai";

describe("AppLoadingScreen", () => {
  let component;

  beforeEach(() => {
    component = shallowMount(AppLoadingScreen);
  });

  it("should render Spinner on mount", () => {
    expect(component.find(Spinner).exists()).to.be.true;
  });
});

The numbers below reference the code AppLoadingScreen.spec.js code block above.

  1. A component spinner is imported
  2. AppLoadingScreen.vue (the Vue component we are testing is imported)
  3. **_shallowMount_**is imported from Vue utils. It creates a [**_Wrapper_**](https://vue-test-utils.vuejs.org/api/wrapper/)that contains the mounted and rendered Vue component, but with stubbed child components. A stubbed child component is a replacement for a child component rendered by the component under test. Some more details here on how stubbed components work: https://stackoverflow.com/questions/52962489/what-are-stubbed-child-components-in-vue-test-utils
  4. **_expect_** is imported from chai. expect is a chainable language to construct assertions. Assertions are used to test a specific thing in the code. Some examples of what can be chained to expect to test https://www.chaijs.com/api/bdd/

6. **_describe_**is used to outline what you are testing. This will also show in the terminal after running the test. In this case, we are testing the appLoadingScreen component.

9. **_beforeEach()_** is a Mocha method which executes the callback argument before each of the tests. We run **_shallowMount()_** inside **_beforeEach()_** so a component is mounted before every test.

10. **_shallowMount()_** method is used placing our test component inside.

13. **_it_** is where you perform individual tests. You should be able to describe the tests, in our case “it should render Spinner on mount”. This clearly outlines what the test will be.

14. **_expect_** from chai. Asserts that something should be tested. In our case we look inside our component for the Spinner component, by using **_find_** from vue utils. Find returns a wrapper of the first DOM node or Vue component matching selector. As we are using chai expect, we can chain keywords. In this case, we add **_to.be.true_**

Running the above test **_npm run test:unit_**

Displays the results of the test describe being the “AppLoadingScreen” and it being “should render Spinner on mount”.

How we would go about testing when using Vuex.

A **_.spec.js_** file that uses vuex

import Vuex from "vuex";
import { createLocalVue, shallowMount } from "@vue/test-utils";
import chai, { expect } from "chai";
import sinon from "sinon";
import sinonChai from "sinon-chai";
import Modal from "@/ui/Modal";
import BaseButton from "@/ui/BaseButton";

chai.use(sinonChai);

const localVue = createLocalVue();
localVue.use(Vuex);
localVue.component("BaseButton", BaseButton);

describe("Modal", () => {
  let store;
  const getters = {
    isModalOpen: () => true,
    activeModalName: () => "baz"
  };
  const actions = {
    TOGGLE_MODAL: () => true
  };
  let component;
  const mockMethod = sinon.spy();

  beforeEach(() => {
    store = new Vuex.Store({
      getters,
      actions
    });

    component = shallowMount(Modal, {
      store,
      localVue,
    });
  });

  describe("can close when", () => {
    it("clicking 'x'", () => {
      component.setMethods({ TOGGLE_MODAL: mockMethod });
      component.find(".modal-close-btn").trigger("click");
      expect(mockMethod).to.have.been.called.calledWith({
        isOpen: false,
        name: null
      });
    });
  });
});

The numbers below reference the code Modal.spec.js code block above.

  1. We import vuexas we are going to create a store inside the test file.
  2. createLocalVue from vue utils. Is used to create a local class of vue so we can use components, plugins and mixins without polluting the global vue class.

4. sinon is imported. sinon allows you to create test doubles. For example mock methods from the component we want to pull into the test.

5. sinon-chai is imported. Extends Chai with assertions for the Sinon.JS mocking framework.

6. The modal component is imported (the component we are testing)

7. BaseButton is imported — we have to import this component as it is registered globally.

8. chai.use is called and set to use sinonChai

11. createLocalVue is stored in a variable for reuse.

12. Set the localVueto use vuex.

13. Register the BaseButton component as it was previously registered globally.

16. Create the store variable.

17. Create the getters, matching the component getters. ( here we can change the values to help us test, for example, if something is hidden in v-if we can change to true in the test so it becomes available)

18. Create the actions, matching the component.

25. store a mockMethod using sinon.spy(). A spy call is an object representation of an individual call to a spied function, which could be a fake, spy, stub or mock method.

28. A new store is created passing the getters and actions.

33. We create the shallow mount version of the component with the localVue and store passed in.

41. setMethods allows you to use methods from the store, in this case, we are using the action we created, matching the component file action TOGGLE_MODAL. https://vue-test-utils.vuejs.org/api/wrapper-array/#setmethods

42. find is used to search for a selector in the component. From vue Utils https://vue-test-utils.vuejs.org/api/wrapper/#find

42. trigger is used to pass in the event, in this case ‘click’ is the event that would fire on this selector. https://vue-test-utils.vuejs.org/api/wrapper/trigger.html

43. Here is the test. We use expect to assert that the mockmethod to.have.been.called.calledWith chainable methods come from chai and can be used to test a variety of scenarios. calledWith can check the see what arguments have been passed in, this way we can be sure that what gets passed in does not change otherwise the test will fail and indicate why.

Summary

That’s a basic unit test using mocha and Vue.

As you can imagine each component may have different logic in with new test syntax needed. Vuex is commonly used in Vue apps, which adds another layer of complexity when testing.

With this test, we can check that a store action was called, by testing what would happen if we triggered a click event on a selector.

The store logic itself can be tested independently on its own, which would give you a detailed test of what each action and mutation are doing.

What are the differences between the various JavaScript frameworks? E.g. Vue.js, Angular.js, React.js

What are the differences? Do they each have specific use contexts?

What are the differences? Do they each have specific use contexts?