DEV Community

loading...
Cover image for How to Setup Jest Testing In  Nuxt Js Project

How to Setup Jest Testing In Nuxt Js Project

bawa_geek profile image Lakhveer Bawa ・3 min read

Hello everyone, hope you are doing well and staying safe.

Recently I had to do a lot of work hard to figure out, how to set up the testing environment in Nuxtjs Project. As Nuxtjs does not provide a sufficient built In configuration for the testing environment ( even if you use nuxt CLI to bootstrap the project ). That's why just after finishing up the setup with continue troubleshoot of more than 20 hours. I am going to share my setup in this article. We will assume you have set up the nuxt project using nuxt CLI. if not you can copy and match the package.json from the given Github repo.

Update: Code Refactored in Github Repository to Reduce Duplicating

Github Repo Link

Alt Text

Modify and edit the jest.config.js as below

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

Create the jest.setup.js file and paste the following content into it

import Vue from 'vue'
// import Vuetify from 'vuetify'
import { config } from '@vue/test-utils'

Vue.config.silent = true

// Vue.config.ignoredElements = ['nuxt-link']
// Mock Nuxt components
config.stubs.nuxt = { template: '<div />' }
config.stubs['nuxt-link'] = { template: '<a><slot /></a>' }
config.stubs['no-ssr'] = { template: '<span><slot /></span>' }
Enter fullscreen mode Exit fullscreen mode

Our Jest Setup is done,

Now we are going to write the sample tests

Create tests/nuxt-pages.test.js file with the following content


const { Nuxt, Builder } = require('nuxt')
// eslint-disable-next-line no-unused-vars
const request = require('supertest')
const nuxtConfig = require('../nuxt.config.js')
// We keep the nuxt and server instance
// So we can close them at the end of the test
let nuxt = null

// Init Nuxt.js and create a server listening on localhost:4000
beforeAll(async () => {
  // const config = {
  //   dev: process.env.NODE_ENV === 'production',
  //   rootDir: resolve(__dirname, '../'),
  //   mode: 'universal',
  //   plugins,
  //   modules
  // }

  nuxt = new Nuxt({...nuxtConfig, server: { port: 3001}, buildDir: '.nuxt-build-jest'})

  await new Builder(nuxt).build()

  await nuxt.server.listen(3001, 'localhost')
}, 300000)

// Example of testing only generated html
describe('GET /', () => {
  test('Route / exits and render HTML', async () => {
    const { html } = await nuxt.server.renderRoute('/', {})

    expect(html).toContain('Vuetify')
  })
})

// describe('GET /', () => {
//   test('returns status code 200', async () => {
//     const response = await request(nuxt.server.app).get('/')
//     expect(response.statusCode).toBe(200)
//   })
// })

// describe('GET /test', () => {
//   test('returns status code 404', async () => {
//     const response = await request(nuxt.server.app).get('/test')
//     expect(response.statusCode).toBe(404)
//   })
// })

// Close server and ask nuxt to stop listening to file changes
afterAll(() => {
  nuxt.close()
})
Enter fullscreen mode Exit fullscreen mode

We are testing the Server rendered pages here. for instance, we are checking if index pages contain "Vuetify" text after rendering

Testing the Components

create ./tests/vue-components.test.js

// import { shallowMount } from '@vue/test-utils'
import Logo from '@/components/Logo.vue'
import VuetifyLogo from '@/components/VuetifyLogo'
import GlobalModuleUsage from '@/components/GlobalModuleUsage'

import helpers from '~/utils/GeneralHelpers'

export const addVuetify = (context) => {
  context.vuetify = require('vuetify')
  context.vue.use(context.vuetify)
  // eslint-disable-next-line new-cap
  context.vuetifyInstance = new context.vuetify()
}

export const addVuex = (context) => {
  context.vuex = require('vuex')
  context.vue.use(context.vuex)
}

export const addHelpers = () => {
  return (context) => {
    context.vue.prototype.$helpers = helpers
  }
}

export const addI18n = (options) => {
  return (context) => {
    context.i18n = require('vue-i18n')
    context.vue.use(context.i18n)
    // eslint-disable-next-line new-cap
    context.i18nInstance = new context.i18n(options)
  }
}

export const addFilter = (name, lambda) => {
  return context => context.vue.filter(name, lambda)
}

export const compositeConfiguration = (...configs) => {
  return context => configs.forEach(config => config(context))
}

export const bootstrapVueContext = (configureContext) => {
  const context = {}
  const teardownVueContext = () => {
    jest.unmock('vue')
    Object.keys(context).forEach(key => delete context[key])
    jest.resetModules()
  }

  jest.isolateModules(() => {
    context.vueTestUtils = require('@vue/test-utils')
    context.vue = context.vueTestUtils.createLocalVue()

    jest.doMock('vue', () => context.vue)

    configureContext && configureContext(context)
  })

  return {
    teardownVueContext,
    ...context
  }
}

// describe('Logo', () => {
//   test('is a Vue instance', () => {
//     const wrapper = shallowMount(Logo)
//     expect(wrapper.vm).toBeTruthy()
//   })
// })

describe('Logo', () => {
  let vueContext = null

  beforeEach(() => {
    vueContext = bootstrapVueContext(
      compositeConfiguration(addVuex, addVuetify, addHelpers())
    )
  })

  afterEach(() => {
    vueContext.teardownVueContext()
  })

  test('Test Logo Component', () => {
    const wrapper = vueContext.vueTestUtils.shallowMount(Logo, {
      localVue: vueContext.vue,
      vuetify: vueContext.vuetifyInstance
    })

    expect(wrapper.text()).toMatch('Logo')
  })

  test('Test Vuetify Logo Component', () => {
    const wrapper = vueContext.vueTestUtils.shallowMount(VuetifyLogo, {
      localVue: vueContext.vue,
      vuetify: vueContext.vuetifyInstance
    })

    expect(wrapper.text()).toMatch('Logo')
  })

  test('Test Global Variables', () => {
    const wrapper = vueContext.vueTestUtils.shallowMount(GlobalModuleUsage, {
      localVue: vueContext.vue,
      vuetify: vueContext.vuetifyInstance
    })

    expect(wrapper.text()).toMatch('91')
  })
})
Enter fullscreen mode Exit fullscreen mode

Update: Code Refactored in Repository to Reduce Code Duplicating

Github Repository with all the tests and configuration

Github Repo Link

Important Links:
https://medium.com/@brandonaaskov/how-to-test-nuxt-stores-with-jest-9a5d55d54b28
https://itnext.io/testing-real-world-vuejs-apps-d3e44118f8ce

Discussion (3)

pic
Editor guide
Collapse
stalker3343 profile image
Artem

Hello. Thank you for the guide, it helped me a lot. During set up the testing, I found that nuxt is trying to build components and pages while preparing the store. You need add "ignore" property in finalConfig

const finalConfig = Object.assign({}, nuxtConfig, resetConfig, {
server: { port: constants.port },
buildDir: constants.buildDir,
ignore: ["/components//", "/layouts//", "/pages//*"]
})

Collapse
slidenerd profile image
slidenerd

how to test getters actions and mutations of vuex using this approach?

Collapse
slidenerd profile image
slidenerd • Edited

i found this file github.com/bawa93/nuxtjs-vue-vueti... which has tests for getters, could you kindly add an example for actions and mutations. also where is your buildDir: '.nuxt-build-jest'?