Skip to content

Instantly share code, notes, and snippets.

@lsapan
Created December 27, 2019 21:53
Show Gist options
  • Save lsapan/3bfd0ffc0fb3d4a036fce84f6eea142e to your computer and use it in GitHub Desktop.
Save lsapan/3bfd0ffc0fb3d4a036fce84f6eea142e to your computer and use it in GitHub Desktop.
Setting up Vue tests (with coverage) for mocha and Webpack

Setting up Vue tests (with coverage) for mocha and Webpack

There are seemingly a few incompatibilities with Vue and Istanbul/nyc at the moment, making it a bit difficult to set up proper coverage reporting for Vue files. Thankfully, there's a relatively easy way to fix that!

This will walk you through everything you need to do to add tests and coverage reporting to your vue-cli based project.

  1. Install dependencies for testing and coverage reporting

    yarn add -D @vue/cli-plugin-unit-mocha @vue/test-utils istanbul-instrumenter-loader nyc
    

    Note: You'll also want to install an assertion library like expect or chai, as well as something like sinon for mocking / spying.

  2. Create a .nycrc file at the root of your project:

    {
        "extension": [
          ".js",
          ".vue"
        ],
        "instrument": false,
        "sourceMap": false
    }
    
  3. Create a tests directory, and create an .eslintrc.js file within it:

    module.exports = {
        env: {
            mocha: true,
        },
    }
    
  4. Add a test command to your package.json file:

    {
      "scripts": {
          ...
          "test": "nyc --reporter=html --reporter=text-summary vue-cli-service test:unit src/**/*/*.{js,vue} tests/**/*.spec.js"
      }
    }

    Note: I'd recommend adding the -A flag in there as well, but that's unrelated.

  5. Now for the fun part, let's update our vue.config.js file:

    const { execSync } = require('child_process')
    
    module.exports = {
        ...
        
        chainWebpack: (config) => {
            if (process.env.NODE_ENV === 'test') {
                execSync("sed -i '' 's/source: pathutils.relativeTo(start.source, origFile),/source: origFile,/' node_modules/istanbul-lib-source-maps/lib/get-mapping.js")
    
                config.devtool('cheap-module-eval-source-map')
                config.module.rule('js')
                    .test(/\.js$/)
                    .use('istanbul-instrumenter-loader')
                    .loader('istanbul-instrumenter-loader')
                    .before('babel-loader')
                    .options({
                        esModules: true,
                    })
            }
        }
    }

That's it! You can now run yarn test or npm run test to test your code! The configuration above assumes you create *.spec.js files in your tests directory, but feel free to change it to suit your specific needs.

You may be wondering how or why this works. The config lines probably look reasonable enough, but what in heck is with that execSync line? Well, for some reason, istanbul ends up duplicating the path for .vue files, which results in not being able to see coverage for them. There's a bunch of issues about this, but the general consensus is to change devtool to eval, which removes the duplication from the path.

The problem with using eval though, is that the line numbers and highlighting gets thrown off for ALL files. Seems like a lot of folks are just accepting that, but we can actually use a proper devtool if we change the offending line in istanbul's code.

I'm sure they have that line in there for a reason (there are so many uses cases), but our use case doesn't need it. It also means we can't really submit a PR. Instead, we can just change the source code locally automatically, which is what that disgusting line does. When this eventually gets fixed, you can remove it!

@alan1111
Copy link

alan1111 commented Apr 1, 2020

@lsapan
I solved it by update the query

execSync("sed -i 's/path.resolve(path.dirname(origFile), file)/file/' node_modules/istanbul-lib-source-maps/lib/pathutils.js")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment