Raphael Ugwu Writer, Software Engineer and a lifelong student.

Component testing in Nuxt.js

5 min read 1402

Component testing in Nuxt.js

Recently, Vue has had an increase in popularity which has fueled an interest in Nuxt.js – a framework used to build robust and universal applications with Vue. While building robust applications, it’s important to provide a clear path towards testing as this will reduce the time spent debugging and code refactoring. In this article, we’re going to take a look at how we can set up a game store application with Nuxt.js and test its components. The main prerequisite in understanding this post is the basic knowledge of Vue and Nuxt.js.

Setting up a Nuxt.js application

Our first step to creating an application with Nuxt.js would be to install it in the folder of our project. In the terminal, let’s navigate to our 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 5.2.0):

npx create-nuxt-app game-store

This will take us through a list of options – here your options may differ, below is a guide detailing what was selected in creating the application we’ll be working with:

? Project name: game-store
? Programming language: JavaScript
? Package manager: NPM
? UI Framework: None
? 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

Once this is done, our window should appear:

game store window

We should also have a project folder structure similar to this:

project folder structure

Configuring our store

To manage state efficiently, Nuxt can leverage the abilities of Vuex. This enables every file created in the /store directory to be treated as a Vuex module (i.e it contains its own 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 – here’s a sample:

We made a custom demo for .
No really. Click here to check it out.

// store/games.js

const games = [
    {
      title: "Star Wars Battlefront 2",
      console: "PlayStation 4",
      rating: 7,
      price: 15.30,
      photo: 'https://res.cloudinary.com/fullstackmafia/image/upload/v1604990005/SWBF2_box_or6x8s.jpg'
    },

    {
      title: "BioShock: The Collection",
      console: "PlayStation 4",
      rating: 9,
      price: 16.00,
      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.70,
      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.90,
      photo: 'https://res.cloudinary.com/fullstackmafia/image/upload/v1604990231/56c494ad88a7e300458b4d5a_qeyro6.jpg'
    }
  ];

Next, we’ll configure the state, mutation, action, and getters of this file – we want our store to display only PlayStation 4 titles:

// 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.30
      });
    }
  };
  
  export default { state, mutations, actions, getters };

Next, we’ll map the state of our store to the browser. This will be done by replacing the default view we have in pages/index.vue:

<!-- pages/index.vue -->
<template>
      <v-flex 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-flex>
</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 take a look at how this appears in the browser. Navigate to your terminal and run npm run dev – your browser view should be similar to this:

list of games in grid with title, rating, and price

Configuring our testing framework

The testing framework for our application is Jest (this was chosen earlier during setup). As we’ve previously noticed, Nuxt builds all the content of our store into Vuex modules. The aim here is to have the ability to:

  • Have various stores which are responsible for different functions
  • Be able to test these stores in the same way they are being used in our components (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 tests are run. This way, we are able to select the particular store we want to test. In the Jest config file below we set the globalSetup module to run our Jest setup file first before running any test:

// jest.config.js

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

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, {
  mode: "spa",
  srcDir: nuxtConfig.srcDir,
  ignore: ["**/components/**/*", "**/layouts/**/*", "**/pages/**/*"]
})

const buildNuxt = async () => {
  const nuxt = new Nuxt(config)
  await new Builder(nuxt).build()
  return nuxt
}

module.exports = async () => {
  const nuxt = await buildNuxt()

  process.env.buildDir = nuxt.options.buildDir
}

In the above setup file, 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. Once this is done, we’ll proceed to write the test for our store:

// store/games.test.js

import _ from "lodash"
import Vuex from "vuex"
import { createLocalVue } from "@vue/test-utils"
describe("store/games/games", () => {
  const localVue = createLocalVue()
  localVue.use(Vuex)
  let NuxtStore
  let store
  beforeAll(async () => {
    const storePath = `${process.env.buildDir}/store.js`
    NuxtStore = await import(storePath)
  })
  beforeEach(async () => {
    store = await NuxtStore.createStore()
  })
}

In the above written tests, we used Jest’s beforeAll block to import our built store. The beforeEach block ensures that a new store is created each time a separate test is to be run. Next, we’ll write out the specific tests we want for our application. Let’s say we want a specific set of criteria such as:

  • The video game DOOM to only come in PlayStation 4 titles
  • The video game Star Wars Battlefront 2 to cost exactly $15.30
  • The store to display only video games with a rating of 9
describe("consoleType", () => {
    let playstationfour
    beforeEach(() => {
      playstationfour = store.getters['games/games/playstationfour']
    })
    test("DOOM should be on only playStation 4", () => {
      expect(playstationfour).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            console: 'PlayStation 4',
            title: 'DOOM'
          })
        ])
      )
    })
  })

  describe('cheapGames', () => {
    let cheapGames
    beforeEach(() => {
      cheapGames = store.getters['games/games/cheapGames']
    })
    test(`StarWars BattleFront must cost exactly ${15.30}`, () => {
      expect(cheapGames).toEqual(
        expect.arrayContaining([
          expect.objectContaining({
            price: 15.30
          })
        ])
      )
    })
  })

  describe('bestGames', () => {
    let bestGames
    beforeEach(() => {
      bestGames = store.getters['games/games/bestGames']
    })
    test('Display only the best titles we have', () => {
      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 expected results:
game.test.js pass and logo.spec.js pass

Summary

Writing tests for universal applications can seem cumbersome. A general rule of thumb is to always keep tests simple and concise – this guide can assist with that. Should you need to view the code for our demo, here’s the link on GitHub. You can also check out this link for more info.

: Full visibility into your web 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 apps.

.
Raphael Ugwu Writer, Software Engineer and a lifelong student.

Leave a Reply