@djirdehh

A Full Day of Vue.js

06. Testing

Testing

Testing can help reveal bugs before they appear, instill confidence in your web application, and make it easy to onboard new developers on an existing codebase.

Testing - Basics

Unit Testing

Tests are made to parts of an application in isolation.

End to End Testing

Tests focus on whether an application has been built appropriately from start to finish.

Two main ways to test web applications

2

1

Many unit test environments/suites exist

  • Mocha: A JavaScript testing framework. Allows us to specify our test suites with describe and it blocks.

  • Chai: A testing assertion library. Provides interfaces for us to create assertions for our tests (should..., expect..., assert...).

  • Jest: A JavaScript testing framework that comes with an assertion library, mocking support, snapshot testing, etc. with minimal to no configuration/set-up.

and many more....

Testing - Basics

new Calculator()

2 + 2 | we expect 4

5 - 3 | we expect 2

4 x 4 | we expect 16

Testing - Basics

describe('Calculator', () => {
  it('sums 2 and 2 to 4', () => {
    ...
  });

  it('subtracts 5 and 3 to 2', () => {
    ...
  });
});

describe functions segment each logical unit of tests.

it functions test each expectation we'd want to assert.

Testing - Basics

describe('Calculator', () => {
  describe('sum()', () => {
    it('sums 2 and 2 to 4', () => {
      ...
    });
  });
  
  describe('subtract()', () => {
    it('subtracts 5 and 3 to 2', () => {
      ...
    });
  });
});

Testing - Basics

describe('Calculator', () => {
  it('sums 2 and 2 to 4', () => {
    const calc = new Calculator();
    expect(calc.sum(2,2)).toEqual(4);
  });

  it('subtracts 5 and 3 to 2', () => {
    const calc = new Calculator();
    expect(calc.subtract(5,3)).toEqual(2);
  });
});

Testing - Basics

expect(...).toEqual(...);

Unique functions from the testing framework/assertion library (e.g.  Jest) being used.

Testing - Basics

Testing - Basics

The Arrange, Act, Assert (AAA) pattern

describe('Calculator', () => {
  it('sums 2 and 2 to 4', () => {
    const calc = new Calculator();

    const sumResult = calc.sum(2,2);

    expect(sumResult).toEqual(4);
  });
});

Arrange

Act

Assert

Testing - Basics

We'll now look to test a basic Vue component.

list of todos

input to create new todo

element to remove certain todo

1

2

3

Simple version of TodoMVC

Testing - Vue Component

Testing - Vue Component

<template>
  <div id="app">
    <section class="todoapp">
      <header class="header">
        <h1 class="title">todos</h1>
        <input class="new-todo"
          placeholder="What needs to be done?"
          v-model="newTodo"
          @keyup.enter="addTodo">
      </header>
      <section class="main">
        <ul class="todo-list">
          <li class="todo"
             v-for="(todo, index) in todos"
             :key="index">
            <div class="view">
              <label>{{ todo }}</label>
              <button class="destroy" @click="removeTodo(todo)"></button>
            </div>
          </li>
        </ul>
      </section>
    </section>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      todos: [],
      newTodo: ''
    }
  },
  methods: {
    addTodo() {
      if (this.newTodo !== '') {
        this.todos.push(this.newTodo);
        this.newTodo = '';
      }
    },
    removeTodo(todo) {
      this.todos.splice(this.todos.indexOf(todo), 1);
    }
  }
};
</script>
App.vue
describe('App.vue', () => {
  it('should render correct initial content', () => {
    ...
  });
});

Testing - Vue Component

Testing - Vue Component

import App from "./App";

describe("App", () => {
  it("should render correct initial content", () => {
    // ...
  });
});
App.spec.js

Testing - Vue Component

To assert the HTML content of our component - we need to have our component mounted().

Before we have our component  mounted() , we can create the component constructor.

Testing - Vue Component

import Vue from "vue";
import App from "./App";

describe("App", () => {
  it("should render correct initial content", () => {
    const Constructor = Vue.extend(App);
  });
});
App.spec.js

Testing - Vue Component

import Vue from "vue";
import App from "./App";

describe("App", () => {
  it("should render correct initial content", () => {
    const Constructor = Vue.extend(App);
    const vm = new Constructor().$mount();
  });
});
App.spec.js

Testing - Vue Component

import Vue from "vue";
import App from "./App";

describe("App", () => {
  it("should render correct initial content", () => {
    const Constructor = Vue.extend(App);
    const vm = new Constructor().$mount();

    expect(vm.$el.querySelector(".title").textContent).toBe("todos");
    expect(vm.$el.querySelector(".new-todo").placeholder).toBe(
      "What needs to be done?"
    );
  });
});
App.spec.js

Testing - Vue Component

describe('App.vue', () => {
  it('should render correct initial content', () => {
    ...
  });

  it('should set correct default data', () => {
    ...
  });
});

Testing - Vue Component

todos: [ ] ,
newTodo: ' '

import Vue from "vue";
import App from "./App";

describe("App", () => {
  it("should render correct initial content", () => {
    // ...
  });

  it("should set correct default data", () => {
    const initialData = App.data();
  });
});
App.spec.js

Testing - Vue Component

import Vue from "vue";
import App from "./App";

describe("App", () => {
  it("should render correct initial content", () => {
    // ...
  });

  it("should set correct default data", () => {
    const initialData = App.data();

    expect(initialData.todos).toEqual([]);
    expect(initialData.newTodo).toEqual("");
  });
});
App.spec.js

Testing - Vue Component

vue-test-utils?

Testing - Vue Component

Testing - vue-test-utils

vue-test-utils is a utility library that provides useful methods that help make testing easier.

Testing - vue-test-utils

We're able to mount a component by simply doing:

const wrapper = mount(Component);
import { mount } from "@vue/test-utils";

 

Testing - vue-test-utils

const vm = wrapper.vm;
const html = wrapper.html();
const button = wrapper.find('button');
  • Access the component instance:

  • Retrieve component's html:

  • Find element (wrapper):

  • Get Props:

const propValue = wrapper.props('propKey');
  • Trigger Event:

wrapper.trigger('click');
  • Set Methods:

wrapper.setMethods({});

and a lot more...

Testing - vue-test-utils

Testing - vue-test-utils

.html()

.setProps()

.setData()

.setMethods()

.vm

component

wrapper

Testing - vue-test-utils

mount()

Creates a Wrapper that contains the mounted Vue component.

shallowMount()

Creates a Wrapper that contains the mounted Vue component, but with stubbed child components.

import { mount, shallowMount } from "@vue/test-utils";
import { mount } from "@vue/test-utils";

Testing - vue-test-utils

Tests the component in isolation.

shallowMount()

It's faster.

great to use when we want to focus on a single component...

Testing - vue-test-utils

import Vue from "vue";
import App from "./App";

describe("App.vue", () => {
  it("should render correct initial content", () => {
    const Constructor = Vue.extend(App);
    const vm = new Constructor().$mount();

    expect(vm.$el.querySelector(".title").textContent).toBe("todos");
    expect(vm.$el.querySelector(".new-todo").placeholder).toBe(
      "What needs to be done?"
    );
  });

  it("should set correct default data", () => {
    const initialData = App.data();

    expect(initialData.todos).toEqual([]);
    expect(initialData.newTodo).toEqual("");
  });
});
App.spec.js

Testing - vue-test-utils

npm install @vue/test-utils --save-dev

Testing - vue-test-utils

import App from "./App";
import { shallowMount } from "@vue/test-utils";

describe("App.vue", () => {
  it("should render correct initial content", () => {
    ...
  });

  it("should set correct default data", () => {
    ...
  });
});
App.spec.js

Testing - vue-test-utils

import App from "./App";
import { shallowMount } from "@vue/test-utils";

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

  it("should render correct initial content", () => {
    ...
  });

  it("should set correct default data", () => {
    ...
  });
});
App.spec.js

Testing - vue-test-utils

import App from "./App";
import { shallowMount } from "@vue/test-utils";

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

  beforeEach(() => {
    wrapper = shallowMount(App);
  });

  it("should render correct initial content", () => {
    ...
  });

  it("should set correct default data", () => {
    ...
  });
});

Testing - vue-test-utils

import App from "./App";
import { shallowMount } from "@vue/test-utils";

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

  beforeEach(() => {
    wrapper = shallowMount(App);
  });

  it("should render correct initial content", () => {
    expect(wrapper.find(".title").text()).toBe("todos");
    expect(wrapper.find(".new-todo").element.placeholder).toBe(
      "What needs to be done?"
    );
  });

  it("should set correct default data", () => {
    ...
  });
});
App.spec.js

Testing - vue-test-utils

import App from "./App";
import { shallowMount } from "@vue/test-utils";

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

  beforeEach(() => {
    wrapper = shallowMount(App);
  });

  it("should render correct initial content", () => {
    expect(wrapper.find(".title").text()).toBe("todos");
    expect(wrapper.find(".new-todo").element.placeholder).toBe(
      "What needs to be done?"
    );
  });

  it("should set correct default data", () => {
    expect(wrapper.vm.todos).toEqual([]);
    expect(wrapper.vm.newTodo).toEqual("");
  });
});
App.spec.js

Testing - vue-test-utils

input to create new todo

1

Testing - vue-test-utils

describe("App.vue", () => {
  it("should render correct initial content", () => {
    ...
  });

  it("should set correct default data", () => {
    ...
  });

 

  describe("the user populates the text input field", () => {
    ...
  });
});

Testing - vue-test-utils

describe("App.vue", () => {
  ...

 

  describe("the user populates the text input field", () => {
    it("should update the 'newTodo' data property", () => {
      ...
    });
  });
});

Testing - vue-test-utils

import App from "./App";

describe("App", () => {
  // ...

  describe("the user populates the text input field", () => {
    it("should update the 'newTodo' data property", () => {
      const inputField = wrapper.find(".new-todo");
    });
  });
});
App.spec.js

Testing - vue-test-utils

import App from "./App";

describe("App", () => {
  // ...

  describe("the user populates the text input field", () => {
    it("should update the 'newTodo' data property", () => {
      const inputField = wrapper.find(".new-todo");
      
      inputField.element.value = "New Todo";
      inputField.trigger("input");
    });
  });
});
App.spec.js

Testing - vue-test-utils

import App from "./App";

describe("App", () => {
  // ...

  describe("the user populates the text input field", () => {
    it("should update the 'newTodo' data property", () => {
      const inputField = wrapper.find(".new-todo");
      
      inputField.element.value = "New Todo";
      inputField.trigger("input");

      expect(wrapper.vm.newTodo).toEqual("New Todo");
    });
  });
});
App.spec.js

Testing - vue-test-utils

list of todos

2

Testing - vue-test-utils

describe("App.vue", () => {
  ...

 

  describe("the user populates the text input field and presses Enter", () => {
    it("should add a new todo to the 'todos' array", () => {
      ...
    });
  });
});

Testing - vue-test-utils

import App from "./App";

describe("App", () => {
  // ...

  describe("the user populates the text input field and presses Enter", () => {
    it("should add a new todo to the 'todos' array", () => {
      const inputField = wrapper.find(".new-todo");
    });
  });
});
App.spec.js

Testing - vue-test-utils

import App from "./App";

describe("App", () => {
  // ...

  describe("the user populates the text input field and presses Enter", () => {
    it("should add a new todo to the 'todos' array", () => {
      const inputField = wrapper.find(".new-todo");
      
      inputField.element.value = "New Todo";
      inputField.trigger("input");
      inputField.trigger("keyup.enter");
    });
  });
});
App.spec.js

Testing - vue-test-utils

import App from "./App";

describe("App", () => {
  // ...

  describe("the user populates the text input field and presses Enter", () => {
    it("should add a new todo to the 'todos' array", () => {
      const inputField = wrapper.find(".new-todo");
      
      inputField.element.value = "New Todo";
      inputField.trigger("input");
      inputField.trigger("keyup.enter");
      
      expect(wrapper.vm.todos).toEqual(["New Todo"]);
    });
  });
});
App.spec.js

Testing - vue-test-utils

import App from "./App";

describe("App", () => {
  // ...

  describe("the user populates the text input field", () => {
    it("should update the 'newTodo' data property", () => {
      const inputField = wrapper.find(".new-todo");

      inputField.element.value = "New Todo";
      inputField.trigger("input");

      expect(wrapper.vm.newTodo).toEqual("New Todo");
    });
  });

  describe("the user populates the text input field and presses Enter", () => {
    it("should add a new todo to the 'todos' array", () => {
      const inputField = wrapper.find(".new-todo");

      inputField.element.value = "New Todo";
      inputField.trigger("input");
      inputField.trigger("keyup.enter");

      expect(wrapper.vm.todos).toEqual(["New Todo"]);
    });
  });
});
App.spec.js

Testing - vue-test-utils

import App from "./App";

describe("App", () => {
  // ...

  describe("the user populates the text input field", () => {
    let inputField;

    beforeEach(() => {
      inputField = wrapper.find(".new-todo");
      inputField.element.value = "New Todo";
      inputField.trigger("input");
    });

    it("should update the 'newTodo' data property", () => {
      expect(wrapper.vm.newTodo).toEqual("New Todo");
    });

    describe("and presses Enter", () => {
      it("should add a new todo to the 'todos' array", () => {
        inputField.trigger("keyup.enter");
        expect(wrapper.vm.todos).toEqual(["New Todo"]);
      });
    });
  });
});
App.spec.js

Testing - vue-test-utils

 Mixins, Filters, & Testing - Chapter Summary!

  • vue-test-utils is Vue's official testing utility library.

  • Mount and shallow mount components with mount() and shallowMount() functions.

  • Mounted wrapper contains useful methods to help make testing easier:

wrapper.html();

wrapper.find();

wrapper.trigger();

wrapper.setData();

wrapper.setProps();

wrapper.setMethods();

 Mixins, Filters, & Testing - Exercise 💪!

Testing

CodeSandbox

Solution 👀

Made with Slides.com