Skip to content

Instantly share code, notes, and snippets.

@lsapan
Created December 27, 2019 21:53
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • 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!

@rwd
Copy link

rwd commented Feb 25, 2020

@isapan, many thanks for this guide!

Is the sed command meant to have that first blank argument? If I run it with that, it errors and exits. Removing it resolves that, and we then have test coverage.

In case it's relevant:

$ sed --version
sed (GNU sed) 4.8
$ uname -a
Linux abyss 5.5.4-arch1-1 #1 SMP PREEMPT Sat, 15 Feb 2020 00:36:29 +0000 x86_64 GNU/Linux

@lsapan
Copy link
Author

lsapan commented Feb 25, 2020

@rwd good point, thanks! I should've mentioned that the -i '' is due to the fact I'm on Mac.

@MuyBien
Copy link

MuyBien commented Mar 4, 2020

Thank you!!! After many forum and many GitHub issues post, I've finally made it thanks to you!

@alan1111
Copy link

alan1111 commented Apr 1, 2020

@lsapan,
Thanks for your knowledge sharing firstly.
image
However, did you meet the error "Unable to lookup source" in vue coverage file?

@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