.../articles/
Getting Started with Frontend Testing Using Nuxt.js, Jest, and Codecov

Getting Started with Frontend Testing Using Nuxt.js, Jest, and Codecov

2019.09.21

As the title suggests, better late than never — I finally got started with frontend testing.

We use Nuxt.js for many of our projects, so I first added Jest, a JS testing framework, along with Codecov for checking coverage.

Install the necessary packages with the following command.

for Jest

$ yarn add -D jest vue-jest ts-jest babel-jest @vue/test-utils babel-preset-vue-app

for Codecov

$ yarn add -D codecov

Next, add scripts to package.json. Since we want test:ci to output coverage, I've added an option for that.

  "scripts": {
    "test": "jest --config jest.config.js",
    "test:ci": "jest --config jest.config.js --coverage"
  },

This time, I set up a tests directory at the application's root directory and put the files inside it. The mock data also goes there. I've changed the file names, but the directory structure looks something like this.

rootDir
└── 
    tests
    └── unit
        ├── _mockData
        │   └── posts.json
        ├── compounds
        │   └── button
        │       └── ButtonDefault.spec.js
        └── organisms
            └── list
                └── ListButton.spec.js

Next, prepare a config file for Jest, jest.config.js. Since Nuxt uses TypeScript (partially), I've also added preset: 'ts-jest',.

module.exports = {
  preset: 'ts-jest',
  transform: {
    '^.+\\.js$': '<rootDir>/node_modules/babel-jest',
    '.*\\.(vue)$': '<rootDir>/node_modules/vue-jest',
    '^.+\\.ts$': '<rootDir>/node_modules/ts-jest',
  },
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/app/$1',
    '^~/(.*)$': '<rootDir>/app/$1',
  },
  moduleFileExtensions: ['js', 'json', 'vue', 'ts'],
  testMatch: [
    '<rootDir>/tests/unit/**/*.js',
    '<rootDir>/tests/unit/**/*.ts',
  ],
  globals: {
    'ts-jest': {
      tsConfig: 'tsconfig.json',
    },
  },
  collectCoverage: false,
  collectCoverageFrom: ['<rootDir>/app/components/**/*.vue', '<rootDir>/app/pages/**/*.vue'],
  coverageDirectory: './coverage/',
  coverageReporters: ['json', 'text-lcov', 'lcov'],
}

Once all the file setup is done, let's start writing tests.

The file I want to test this time is components/organisms/list/ListButton.vue below.

<template>
  <ul class="list">
    <li
      v-for="(post, i) in posts"
      :key="i"
    >
      <a
        @click="$emit('clickItem', post)"
      >
        {{ post.name }}
      </a>
    </li>
    <li>
      <a
        @click="$emit('clickAdd')"
      >
        Add new
        <svg viewBox="0 0 24 24">
          <use xlink:href="#plus" />
        </svg>
      </a>
    </li>
  </ul>
</template>

<script>
import VueTypes from 'vue-types'

export default {
  props: {
    post: VueTypes.arrayOf(VueTypes.shape({
      name: VueTypes.string.isRequired,
      description: VueTypes.string,
    })).isRequired.loose,
  },
}
</script>

The testing perspectives for this file are:

  • The data passed in via props
  • Whether the click event works correctly
import { mount, shallowMount } from '@vue/test-utils'
import posts from '../../_mockData/posts.json'
import ListButton from '@/components/organisms/list/ListButton.vue'

describe('components/organisms/list/ListButton.vue', () => {
  const propsData = {
    list: posts.list,
  }
  test('props', () => {
    const wrapper = mount(ListButton, { propsData })
    expect(wrapper.props()).toEqual(propsData)
  })
  test('check click link event', () => {
    const wrapper = shallowMount(ListButton, { propsData })
    // Get all the a tags
    const links = wrapper.findAll('a')
    links.wrappers.forEach((link, index, links) => {
      // Only the last a tag has different behavior, so branch the test
      if (index === 0) {
        link.trigger('click')
        // Check that the click event fires
        expect(wrapper.emitted().clickItem).toBeTruthy()
        // Check that the content passed with the click event is as expected
        expect(wrapper.emitted().clickItem[0][0]).toBe(posts.list[0])
      } else if (index === (links.length - 1)) {
        link.trigger('click')
        expect(wrapper.emitted().clickAdd).toBeTruthy()
      }
    })
  })
})

Now, if you run

$ yarn test

locally, it will check the results for you.

Once you've gotten this far, you'll naturally want to check coverage too.

We use CircleCI, so I add the following to the config. (You also need to set up a token in the CircleCI settings screen.) Just with this, coverage gets visualized nicely!

      - run:
          name: Testing and export coverage report
          command: yarn test:ci
      - run:
          name: Codecov
          command: yarn codecov

Here's an example of what gets produced.

And here's another one...... (looks like a scorched earth, huh...? 😇😇😇)

And that's it — Jest + Codecov now lets us check tests and coverage! Seeing that scorched earth really does make you want to write tests!!!!!!!!!!!!!! Uploading coverage to CircleCI's artifacts is sadly something nobody looks at, so I also think it's a benefit that it becomes visible in things like PRs! I highly recommend giving it a try 😎

Going forward, I plan to keep writing tests bit by bit, build up my knowledge, and share it again sometime.

written by

.../article/

Articles

All articles

From Firebase to Vercel, Contentful to microCMS — a migration log written with Claude Code

From Firebase to Vercel, Contentful to microCMS — a migration log written with Claude Code

We moved our corporate site's hosting and CMS, and made it bilingual along the way. The constraints we only found by running against real data were more useful than the migration itself, so this post focuses on where we got stuck.

Can't Read POST Data with Firebase Functions × Remix?

Can't Read POST Data with Firebase Functions × Remix?

How to read POST data from a Remix action when running on Firebase Functions.

Generative AI for Executives and Leaders: An Approach to Self-Driven DX

Generative AI for Executives and Leaders: An Approach to Self-Driven DX

Building a structure where executives and leaders themselves can identify issues and evaluate solutions using generative AI. We introduce how combining this with our hands-on support dramatically improves both the quality and speed of digital transformation.

Deploying a Monorepo Next.js App (App Router) to AWS Amplify

Deploying a Monorepo Next.js App (App Router) to AWS Amplify

Notes on the obstacles we hit while deploying a Next.js app managed in a monorepo to AWS Amplify.

Keeping Production Running Smoothly with Remote Work and Online Meetings [Documentation]

Keeping Production Running Smoothly with Remote Work and Online Meetings [Documentation]

Many production companies have adopted remote work as a result of the pandemic, and we are one of them.

Designing an E-Commerce Site That Sells: How to Find Great Reference Examples

Designing an E-Commerce Site That Sells: How to Find Great Reference Examples

There is no single formula for e-commerce design that sells. Driving revenue requires a solid concept, and getting to that concept requires thorough research.

Productivity Tools We Recommend as a Production Company, Including Services That Work Well Solo

Productivity Tools We Recommend as a Production Company, Including Services That Work Well Solo

With remote work becoming the norm during the COVID-19 pandemic, our team now works from home most days of the week.

We Released Thought Recorder, a Figma Plugin for Keeping a Commit History of Your Designs

We Released Thought Recorder, a Figma Plugin for Keeping a Commit History of Your Designs

We hope this helps web designers who work in Figma. Read on for how to use it.

How to Build an E-Commerce Site, and Which Platforms We Recommend

How to Build an E-Commerce Site, and Which Platforms We Recommend

Shopping online for fashion, appliances, and even groceries is now routine. With the pandemic accelerating the shift, we receive a steady stream of questions about which platform to use and how much it costs.

Generating FastAPI Schema Classes from OpenAPI

Generating FastAPI Schema Classes from OpenAPI

We chose FastAPI, a relatively modern framework, for a Python API project. FastAPI can generate an OpenAPI definition from your backend code, but here we do the opposite: generating FastAPI schema classes from an OpenAPI definition prepared in advance.

View all articles

Contact us