Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Unit vs. E2E Testing for Vue.js

DZone 's Guide to

Unit vs. E2E Testing for Vue.js

Need to understand how to better test projects for Vue.js apps?

· Web Dev Zone ·
Free Resource

Writing tests when developing Vue.js apps can save you time which would otherwise be 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

Unit testing breaks down code into small, easily testable parts. Usually, a 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.

// 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);
});


Anytime 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:

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


As previously stated, for a given input (in this case, a prop), a unit test must 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.

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; you generate the output of the component once and write it to a text file. For example:

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


Now, anytime 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 large amounts of HTML.

E2E Tests

E2E 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 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", "anthony@test.com")
    .setValue("@passwordInput", "test1234")
    .click("@submitButton");

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

}


Unit and E2E Test Comparison

Unit test pros:

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

Unit test cons:

  • Time-consuming to write tests for every aspect of your app.
  • Despite unit tests passing, the whole application may still not work.

E2E test pros:

  • Can implicitly test many things at once.
  • E2E tests assure you that you have a working system.

E2E test cons:

  • Slow to run — will often take 5 or 10 mins to run for one site.
  • Brittle — an inconsequential change, like changing a class, can bring down your entire E2E suite
  • Tests can't pinpoint the cause of failure

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, E2E tests will.

Using these test types together will give you 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:

1. Write unit tests for all your components, including error states. Run these before you make git commits.

2. Write E2E tests for the key use cases of your site e.g. registration, add to cart, etc. Run these before merging to master.

If you want more details on the right mix of tests, there a plenty of good blog posts like the classic Write tests. Not too many. Mostly integration. by Kent C. Dodds.

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!

Topics:
vue.js ,web dev ,unit testing ,e2e testing ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}