Raphael Ugwu Writer, software engineer, and a lifelong student.

Component testing in Nuxt.js with Jest

6 min read 1793

Nuxtjs Component Testing

Editor’s note: This article was last updated 15 November 2022 to include the addition of Vuetify as a UI framework

Vue’s increase in popularity over the past few years has fueled an interest in Nuxt.js, a framework used to build robust and universal applications with Vue. When building applications, it’s important to provide a clear path towards testing, which will reduce the time spent debugging and refactoring code.

In this article, we’ll learn how to set up a game store application with Nuxt.js and test its components. To follow along with this article, you should have basic knowledge of Vue and Nuxt.js. You can also check out the full repository on GitHub. Let’s get started!

Setting up a Nuxt.js application

To create a new application with Nuxt.js, we first have to install it. In the terminal, navigate to your project folder and input the following command:

npm install nuxt

Still using the terminal, in our project folder, we’ll create our application via npx, which is shipped by default since npm ≥v5.2.0:

npx create-nuxt-app game-store

Running this will take us through a list of options, and your options may differ during this process. Select Vuetify as the UI framework and Jest as the testing framework option. The guide below details what we selected in creating the application we’ll be working with:

? Project name: game-store
? Programming language: JavaScript
? Package manager: NPM
? UI Framework: Vuetify
? Nuxt.js modules: None
? Linting tools: None
? Testing framework: Jest
? Rendering mode: Single Page App
? Deployment target: Static
? Development tools: jsconfig.json
? Version Control System: Git

Once we’re done creating the application, we can navigate to it via our terminal and launch it in the browser:

cd game-stores
npm run dev

The following window should appear:

Nuxtjs Game Store Window

We should also have a project folder structure similar to the one below:

Nuxtjs Project Folder Structure

Configuring our store

To efficiently manage states, Nuxt.js leverages the abilities of Vuex. Therefore, every file created in the /store directory can be treated as a Vuex module, meaning it contains its state, mutation, action, and getters.

We’ll use our store directory as a starting point for our application. Let’s begin by including the data we need. Create a new games/games.js file in the existing /store directory and paste the following code into it:

// store/games/games.js

const games = [
  {
    title: "Star Wars Battlefront 2",
    console: "PlayStation 4",
    rating: 7,
    price: 15.3,
    photo:
      "https://res.cloudinary.com/fullstackmafia/image/upload/v1604990005/SWBF2_box_or6x8s.jpg",
  },
  {
    title: "BioShock: The Collection",
    console: "PlayStation 4",
    rating: 9,
    price: 16.0,
    photo:
      "https://res.cloudinary.com/fullstackmafia/image/upload/v1604990078/220px-BioShock-_The_Collection_tix1ol.jpg",
  },
  {
    title: "Call of Duty: Black Ops 4",
    console: "PlayStation 4",
    rating: 9,
    price: 11.7,
    photo:
      "https://res.cloudinary.com/fullstackmafia/image/upload/v1604990123/220px-Call_of_Duty_Black_Ops_4_official_box_art_vvhd7w.jpg",
  },
  {
    title: "Tom Clancy's Rainbow Six: Siege",
    console: "PlayStation 5",
    rating: 9,
    price: 13.9,
    photo:
      "https://res.cloudinary.com/fullstackmafia/image/upload/v1604990231/56c494ad88a7e300458b4d5a_qeyro6.jpg",
  },
];

Next, we’ll configure our games store’s state, mutation, action, and getters. To perform this operation, in the games.js file we created earlier, paste the following code immediately after the former code snippet:

// store/games/games.js

const state = () => {
  return games;
};

const mutations = {};
const actions = {};
const getters = {
  bestGames(state) {
    return state.filter(({ rating }) => {
      return rating === 9;
    });
  },
  playstationfour(state) {
    return state.filter(({ console }) => {
      return console === "PlayStation 4";
    });
  },
  consoleType(state) {
    return (consoleName) => {
      return state.filter(({ console }) => {
        return console === consoleName;
      });
    };
  },
  cheapGames(state) {
    return state.filter(({ price }) => {
      return price === 15.3;
    });
  },
};

export default { state, mutations, actions, getters };

Let’s display our games store data on the main page by iterating through each item using the v-for directive. Replace the code in pages/index.vue with the following:

<!-- pages/index.vue -->
<template>
  <div>
    <v-container>
      <v-row>
        <v-col xs4 v-for="game in psfourGames" :key="game.title">
          <v-card>
            <v-img :src="game.photo" aspect-ratio="1"></v-img>
            <v-card-title primary-title>
              <div>
                <h3>{{ game.title }}</h3>
                <h4>Rating: {{ game.rating }}</h4>
                <h4>Price: ${{ game.price }}</h4>
              </div>
            </v-card-title>
          </v-card>
        </v-col>
      </v-row>
    </v-container>
  </div>
</template>

Then, we’ll use Vuex’s MapGetter helper to map the previously defined getter in games.js to a computed property in our index.vue file:

<!-- pages/index.vue -->
<script>
import { mapGetters } from "vuex";
export default {
  computed: {
    ...mapGetters({
      consoleType: "games/games/consoleType",
    }),
    psfourGames() {
      return this.consoleType("PlayStation 4");
    },
  },
};
</script>

Let’s see how this appears in the browser so far. Navigate to your terminal and run npm run dev. Your browser view should be similar to the image below:

Nuxtjs Browser View Games

Configuring our testing framework

For our application’s testing framework, we’re using Jest, which we selected earlier during setup. As we’ve previously mentioned, Nuxt.js builds all the content of our store into Vuex modules. Our goal is to have various stores that are responsible for different functions and to test these stores in the same way that they are used in our components, meaning we choose which particular store we want to test.

To achieve this, we’ll configure Jest to use its globalSetup module, which exports an async function that is triggered once before all the tests are run. Therefore, we can choose the particular store that we want to test. To perform this operation, replace the code in the jest.config.js file with the following:

// jest.config.js

module.exports = {
  globalSetup: "&lt;rootDir&gt;/jest.setup.js",
  moduleNameMapper: {
    "^@/(.*)$": "&lt;rootDir&gt;/$1",
    "^~/(.*)$": "&lt;rootDir&gt;/$1",
    "^vue$": "vue/dist/vue.common.js",
  },
  moduleFileExtensions: ["js", "vue", "json"],
  transform: {
    "^.+\\.js$": "babel-jest",
    ".*\\.(vue)$": "vue-jest",
  },
  collectCoverage: true,
  collectCoverageFrom: [
    "&lt;rootDir&gt;/components/**/*.vue",
    "&lt;rootDir&gt;/pages/**/*.vue",
  ],
};

In the Jest config file above, we’ve configured the globalSetup module to run our Jest setup file first before running any tests.

Next, we’ll create the jest.setup.js file, where we’ll expose the directory of our store through a process variable:

import { Nuxt, Builder } from "nuxt";
import nuxtConfig from "./nuxt.config";

const resetConfig = {
  loading: false,
  loadingIndicator: false,
  fetch: {
    client: false,
    server: false,
  },
  features: {
    store: true,
    layouts: false,
    meta: false,
    middleware: false,
    transitions: false,
    deprecations: false,
    validate: false,
    asyncData: false,
    fetch: false,
    clientOnline: false,
    clientPrefetch: false,
    clientUseUrl: false,
    componentAliases: false,
    componentClientOnly: false,
  },
  build: {
    indicator: false,
    terser: false,
  },
};
const config = Object.assign({}, nuxtConfig, resetConfig, {
  srcDir: nuxtConfig.srcDir,
  ignore: ["**/components/**/*", "**/layouts/**/*", "**/pages/**/*"],
});
const buildNuxt = async () =&gt; {
  const nuxt = new Nuxt(config);
  await new Builder(nuxt).build();
  return nuxt;
};
module.exports = async () =&gt; {
  const nuxt = await buildNuxt();
  process.env.buildDir = nuxt.options.buildDir;
};

In the setup file above, resetConfig ensures that only the store is built when we run our build process. Then, we use process.env.buildDir to expose the path for our store.

With that completed, let’s write the test for our store. Create a new games.test.js file inside the existing /test directory and add the following code to it:

// test/games.test.js

import _ from "lodash";
import Vuex from "vuex";
import { createLocalVue } from "@vue/test-utils";

describe("store/games/games", () =&gt; {
  const localVue = createLocalVue();
  localVue.use(Vuex);
  let NuxtStore;
  let store;
  beforeAll(async () =&gt; {
    const storePath = `${process.env.buildDir}/store.js`;
    NuxtStore = await import(storePath);
  });
  beforeEach(async () =&gt; {
    store = await NuxtStore.createStore();
  });
// ...
});

In the tests above, we used Jest’s beforeAll block to import the built store. The beforeEach block ensures that a new store is created each time a separate test is run.

Next, we’ll write out the specific tests we want for our application. Let’s say we want the following specific set of criteria:

  • The video game Call of Duty: Black Ops 4 only comes in PlayStation 4 titles
  • The video game Star Wars Battlefront 2 costs exactly $15.30
  • The store displays only video games with a rating of 9

Our test code will be like the following:

describe("consoleType", () =&gt; {
  let playstationfour;
  beforeEach(() =&gt; {
    playstationfour = store.getters["games/games/playstationfour"];
  });
  test("Call of Duty: Black Ops 4 should be on only playStation 4", () =&gt; {
    expect(playstationfour).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          console: "PlayStation 4",
          title: "Call of Duty: Black Ops 4",
        }),
      ])
    );
  });
});

describe("cheapGames", () =&gt; {
  let cheapGames;
  beforeEach(() =&gt; {
    cheapGames = store.getters["games/games/cheapGames"];
  });
  test(`StarWars BattleFront must cost exactly ${15.3}`, () =&gt; {
    expect(cheapGames).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          price: 15.3,
        }),
      ])
    );
  });
});

describe("bestGames", () =&gt; {
  let bestGames;
  beforeEach(() =&gt; {
    bestGames = store.getters["games/games/bestGames"];
  });
  test("Display only the best titles we have", () =&gt; {
    expect(bestGames).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          rating: 9,
        }),
      ])
    );
  });
});

However, you must ensure that the test code is written immediately after the beforeEach block so that we can continue to access our store instance. With these new changes, the complete code for our games.test.js file will look like the following:

// test/games.test.js

import _ from "lodash";
import Vuex from "vuex";
import { createLocalVue } from "@vue/test-utils";

describe("store/games/games", () =&gt; {
  const localVue = createLocalVue();
  localVue.use(Vuex);
  let NuxtStore;
  let store;
  beforeAll(async () =&gt; {
    const storePath = `${process.env.buildDir}/store.js`;
    NuxtStore = await import(storePath);
  });
  beforeEach(async () =&gt; {
    store = await NuxtStore.createStore();
  });

  describe("consoleType", () =&gt; {
    let playstationfour;
    beforeEach(() =&gt; {
      playstationfour = store.getters["games/games/playstationfour"];
    });
    test("Call of Duty: Black Ops 4 should be on only playStation 4", () =&gt; {
      expect(playstationfour).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            console: "PlayStation 4",
            title: "Call of Duty: Black Ops 4",
          }),
        ])
      );
    });
  });

  describe("cheapGames", () =&gt; {
    let cheapGames;
    beforeEach(() =&gt; {
      cheapGames = store.getters["games/games/cheapGames"];
    });
    test(`StarWars BattleFront must cost exactly ${15.3}`, () =&gt; {
      expect(cheapGames).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            price: 15.3,
          }),
        ])
      );
    });
  });

  describe("bestGames", () =&gt; {
    let bestGames;
    beforeEach(() =&gt; {
      bestGames = store.getters["games/games/bestGames"];
    });
    test("Display only the best titles we have", () =&gt; {
      expect(bestGames).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            rating: 9,
          }),
        ])
      );
    });
  });
});

Let’s give our tests a go. Navigate to your terminal and run npm test; this should run all the specified tests and deliver the expected results, reading All default components and routes have been deleted before running this test:

Game Tests Nuxtjs Pass
game.test.js pass

Conclusion

Writing tests for universal applications can seem cumbersome. A general rule of thumb is to always keep tests simple and concise. In this article, we explored testing our Nuxt.js components with Jest. We also explored Vuetify and Vuex. I hope you enjoyed this article, and be sure to leave a comment if you have questions. Happy coding!

: Full visibility into your web and mobile apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

Experience your Vue apps exactly how a user does

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps - .

.
Raphael Ugwu Writer, software engineer, and a lifelong student.

One Reply to “Component testing in Nuxt.js with Jest”

  1. Thanks for the walkthrough! Here are some things I tweaked while following along.

    – Added Vuetify so that all the `v-` components work. Easiest to do this during the `npx create-nuxt-app` step.
    – Changed the `v-flex` component to `v-col`, which I guess is the Vuetify 2.x replacement.
    – Wrapped the `v-col` in a `v-row`, both to make the layout work and because the template has to have a single root element.
    – Changed the path in `mapGetters` to `’games/consoleType’`, since the store file is `store/games.js`.
    – Likewise, removed the extra `games` from the getter paths in the tests.
    – Added the missing parenthesis to the end of the test file.
    – Removed `mode` from `jest.setup.js` – it’s for an older version of Nuxt.
    – Changed `’DOOM’` to `’Call of Duty: Black Ops 4’`, since DOOM isn’t in the store file.

Leave a Reply