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 which i used for our website acting jobs in india. 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
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',
],
}
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>' }
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()
})
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')
})
})
Update: Code Refactored in Repository to Reduce Code Duplicating
Github Repository with all the tests and configuration
Important Links:
https://medium.com/@brandonaaskov/how-to-test-nuxt-stores-with-jest-9a5d55d54b28
https://itnext.io/testing-real-world-vuejs-apps-d3e44118f8ce
Top comments (6)
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//*"]
})
Awesome post! I was struggling with the same just with Nuxt3 which adds some new 'funny' tricks to mix. 😅
I wrote about it here: medium.com/@fgoessler/set-up-nuxt3...
Thank you for sharing these valuable content on Jest testing ! I've been exploring different resources to enhance my test automation skills, I recently came across another article titled 'Complete guide to Jest testing' that expands on some of the concepts you've covered here. It delves deeper into practical tips and techniques for optimizing test automation workflows with Jest. Keep up the great work!"
how to test getters actions and mutations of vuex using this approach?
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'?
Why are you creating a build for testing, what is the benefits of that?