Vue.js Testing Resources

Found a few helpful resources on testing applications running the Vue framework. Logging here for reference, because I have too many tabs open.

Vue Testing Handbook
https://lmiller1990.github.io/vue-testing-handbook/setting-up-for-tdd.html#making-assertions

Vue Test Utils
https://vue-test-utils.vuejs.org/installation/#using-vue-test-utils-with-jest-recommended

North 47 KB Article — Vue 2 and Jest Testing
https://www.north-47.com/knowledge-base/starting-with-unit-testing-using-vue-js-2-and-jest/

TestDriven.io — Guide to Unit Testing Vue Components
https://testdriven.io/blog/vue-unit-testing/

Digital Ocean Tutorial — How To Test Vue Components with Jest
https://www.digitalocean.com/community/tutorials/vuejs-vue-testing

Doximity Article — Five Traps to Avoid While Unit Testing Vue.js (2017)
https://technology.doximity.com/articles/five-traps-to-avoid-while-unit-testing-vue-js

Testing Library — jest-dom
https://github.com/testing-library/jest-dom

Basic Vue Component Recipe and Workflow

A simple recipe to add a component to a Vue project or application.

Basic File Structure

Add component file in the components/ diectory on the same project level as the main App.vue and main.js, note the Vue.js Style Guide essential rule on using multi-word component names. This rule prevents potential clashes with HTML elements, which are specfied as single words

src
├── App.vue
├── components
│   └── MyComponent.vue
└── main.js

Refer to these sources to get more background on Vue project structure:

Article on itnext.io by Sandoche ADITTANE
Application structure principles for Vuex
Vue.js Style Guide

Basic Development Workflow

A good basic Vue development workflow to follow takes 3 steps:

  1. Add structure
  2. Add logic
  3. Add style

1. Add Structure

The structure of a Vue component is defined in its <template> tag. Note that the <template> tag can contain only one root level element. In the following example, it is the first (and only) <div> element:

<template>
  <div>
    <h1>My Component</h1>
    <p>My component paragraph text</p>
    <button>Button</button>
  </div>
</template>
//...

Besides the HTML structure, Vue will also need some application configuration to use the new component.

Import and register the component in the main.js file:

//...
import MyComponent from './components/MyComponent';
//...
app.component('my-component', MyComponent);
//...

And finally, add the component tag to the App.vue file:

<template>
    <div id="app">
        <h1>My App</h1>
        <my-component />
    </div>
</template>

As a side note, pay attention to the deliberate use of kebab-case and camelCase or PascalCase. The case used is important to allow Vue to run code as HTML in some instances, and Javascript in others.

2. Add Logic

Before writing any code for the logic, consider what functionality the new component will need.

  • What data will be sent from the parent App.vue to the component?
  • What data will the component need to send to the rest of the app?
  • Which parts of the component will be dynamic or interactive?
  • Which methods or functions will the main app and the component need to communicate with each other?

Props

Props allow the parent app to pass data to its children components. In its simplest form, props are added to a component in three parts:

Include the props option and the prop attribute in the component script:

<script>
export default {
  props: ["textContent"],
//...

Add the data to pass to the app, as a custom attribute of the component:

<template>
//...
  <my-component text-content="Lorem ipsum dolor sit amet." />  

Render the data dyamically in the component, with a template tag:

<template>
//...
    <p>{{ textContent }}</p>

Now the parent app sends the text-content data to the child component, and Vue renders the result to the page.

Click Event

Adding a click event to the component markup is relatively straightforward:

<template>
  <div>
    <h1>My Component</h1>
    <p>{{ textContent }}</p>
    <button @click="replaceText">Replace Text</button>
  </div>
</template>

It is also a good opportunity to change the button label, as the nature of the button event is clarified.

The tricky part comes next, to properly handle passing data changes back to the parent app.

Emits

Emits send data from the child component back to the parent.

Add the click handler to the component element

Add the this.$emit() to the component methods, with reference arguments for the component click event, and any data to pass to the parent app:

//...
  methods: {
    replaceText() {
      // emit custom event
      this.$emit("replace-text", this.textContent);
    },
  },
//...

Add the event listener attribute to the instance of the component in the parent app:

<template>
    <div id="app">
        <my-component
        :text-content="textString"
        @replaceText="replaceComponentText"
        />
//...

Add a method to the parent app, so the parent can run the method from the child component:

//...
methods: {
    replaceComponentText(currentTextContent) {
      // always replace the text content, i.e. avoid duplicates
      while (currentTextContent === this.textString) {
        const rand = getRandomInt(3);
        if (rand === 0) {
          this.textString = this.textStringA;
        } else if (rand === 1) {
          this.textString = this.textStringB;
        } else if (rand === 2) {
          this.textString = this.textStringC;
        }
      }
    },
  },
//...

Optional, but recommended: define emits with the emits property in the component. This helps clarify and make obvious the purpose and function of the emits in the component:

//...
  emits: ["replace-text"],
//...

3. Add Style

Not just style, but dynamic style…

Bind a class to the element and add style properties that depend on dynamic values

<template>
//...
    <p class="dynamic" :class="textContentClass">{{ textContent }}</p>
//...

Set computed properties to influence the dynamic style values:

<script>
//...
  computed: {
    textContentClass() {
      if (this.textContent[0] === "J") {
        return "green-machine";
      } else if (this.textContent[0] === "W") {
        return "bold-and-spicy";
      } else if (this.textContent[0] === "R") {
        return "cool-as-ice";
      } else {
        return "no-style";
      }
    },
  },
//...

And of course add the styles to the component file:

<style>
.no-style {
  font-weight: normal;
  color: inherit;
}
.bold-and-spicy {
  font-weight: bold;
  color: crimson;
}
.cool-as-ice {
  font-style: italic;
  color: rgb(20, 163, 220);
}
.green-machine {
  font-style: normal;
  color: rgb(13, 114, 9);
}
</style>

Vue.js The Most Fundamental Idea

Vue.js is Declaritive

Declaritive programming — From Wikipedia:

… declaritive programming is a programming paradigm that expresses the logic of a computation without describing its control flow.

In more straightforward terms, declaritive programs say what should happen, rather than how it should happen.

In contrast, imperitive programming specifically lists the steps to perform a function.

Vue.js Declaritive Manifestion

Rather than, for example, using vanilla Javascript to step-by-step: find objects in the DOM, update those objects, etc. — Vue.js takes a simplified, abstracted approach.

  1. Modify HTML tags and structure with special attributes.
  2. Use Vue.js to apply functionality to the HTML with abstracted functions.

An Example of Vue.js Declaritive Rendering

Extracted directly from the guide at vuejs.org — this example provides a most basic demonstration of declaritive rendering.

HTML

<div id="app">
  {{ message }}
</div>

JS

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})

The "declartive’" part of declaritive rendering is written into the HTML of the example.

In English, it says:

"Put the message into the app"

The accompanying Javascript carries out the duties of making it happen.