How to test Nuxt.js asyncData and fetch hooks

How to test Nuxt.js asyncData and fetch hooks

This article is more than four years old. The content may be outdated, especially technical examples or references to tooling.

When you setup a new project with the official create-nuxt-app or when you do it manually you probably setup a testing framework to write unit tests. Most Nuxt.js and Vue applications use the official @vue/test-utils which includes clever methods and options to help you write the best unit tests for your application. You probably tried to test your asyncData or fetch hook but couldn't succeed. This article explains why this is, but also shows how you can unit test these hooks. 

Testing a Vue component

A basic unit test for a basic Vue component just costs a few lines of code with the test utils package. By (shallow) mounting a Vue component you’re able to test properties, computed properties, and methods. The component is reactive if you execute methods of the component or change some properties. A basic test will look something like;

import { shallowMount } from '@vue/test-utils';
import ExampleComponent from './example-component.vue';

describe('Example component', () => {
  it('should test something', () => {
    const wrapper = shallowMount(ExampleComponent);
    expect(wrapper.vm.anyProp).not.toEqual(anyExpectedValue);
    wrapper.vm.myMethod();
    expect(wrapper.vm.anyProp).toEqual(anyExpectedValue);
  });
});

You mount the component, call a method and check if the Vue component reacts as you expect. 

Testing a Nuxt.js component

Nuxt.js components are almost the same as a Vue component. So, a basic test will look the same in most cases. Nuxt.js adds extra hooks to the Vue component API, such as the asyncData and fetch hook to load data before a component is rendered, but also the head object to add dynamic meta data to any page. Since the test utils package is written for Vue components and not for Nuxt.js components, it lacks support for these extra hooks and methods. That means we can't access these hooks and methods through the wrapper in our test, but we can access the hooks or methods directly.

Call hook directly

import ExampleComponent from './example-component.vue';

describe('Example component', () => {
  it('should test something', async () => {
    await ExampleComponent.asyncData();
    expect(something).toEqual(somethingExpected);
  });
});

This method has a downside. The to be tested component is not mounted, so we can't make assertions on its changed state. We can test return values, check if spied on methods are being called, but we can’t test the component on state changes. Another downside is that inside these hooks during test the `this` object is not referring to the current Vue component, since it is not executed as part of a mounted Vue component.

Call hook directly with correct context

The solution is obvious; we should mount the component, and tell the hook what its current context is. We need to tell the hook that the `this` object should be the Vue component. With Function.prototype.call() method we can execute any method with the `this` as first argument. The syntax of the method is;

call(thisArg, arg1, ... , argN);

That means that if we are able to mount the component, and add the state of that mounted component as first argument to the .call() method that we are able to make assertions on the components state. And it also means that `this` during test executing is the component itself, like it is during runtime. 

import { shallowMount } from '@vue/test-utils';
import ExampleComponent from './example-component.vue';

describe('Example component', () => {
  it('should test something', async () => {
    const wrapper = shallowMount(ExampleComponent);
    expect(wrapper.vm.anyProp).not.toEqual(anyExpectedValue);
    await ExampleComponent.asyncData.call(wrapper.vm);
    expect(wrapper.vm.anyProp).toEqual(anyExpectedValue);
  });
});

Comments

There are no comments yet, leave yours below.

Leave a comment

Do you have an addition, question or experience related to this article? Share it below.

Comments are briefly reviewed before they appear.

Read more about:

Starten met unit testen in Vue

Vue wordt door steeds meer developers gebruikt. Laravel geeft out of the box de mogelijkheid om gebruik te maken van Vue en Bootstrap, maar ook React of Angular developers kiezen steeds vaker voor Vue. Na het meewerken bij een aantal projecten waar VueJS wordt ingezet begin ik te merken dat er nauwelijks wordt getest. Meest genoemde redenen zijn dat het testen te veel tijd kost en dat unit testen in de frontend moeilijk is. Volgens een…

Lees verder

Unit testen van private methods in Angular

Het liefst test je alles binnen jouw Angular project, alleen dan weet je dat je code goed werkt na elke verandering. Binnen een component test je naast private en public attributen ook private en public methods. Public methods kun je gemakkelijk testen door in de unit test deze methods direct aan te roepen. Met verschillende parameters test je elk pad binnen deze method en weet je dat de code werkt en blijft werken. Bij private methods…

Lees verder

Code coverage is not a quality metric

You've just finished a new feature. The pull request is approved, the pipeline is green, and your coverage report proudly shows 100%. Great. Right? Well, not necessarily. One of the biggest misconceptions I still encounter in software teams is the belief that a high coverage percentage automatically means high quality software. It doesn't. In fact, I've seen teams proudly report 90%+ coverage while still shipping serious production…

Continue reading