# karma-coverage: Istanbul coverage inside a Karma test run

> karma-coverage is the plugin that turns a Karma run into a coverage report, instrumenting your source with Istanbul before the tests execute and writing the report when they finish. It is a small plugin with a narrow job, and most of its documentation is about two things: which files to instrument, and how to get several report formats out of one run.

**karma-runner/karma-coverage** — A Karma plugin. Generate code coverage.

- Repository: https://github.com/karma-runner/karma-coverage
- Stars: 771 · Forks: 243
- Language: JavaScript
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/karma-runner-karma-coverage

## A preprocessor and a reporter in one package

karma-coverage is a Karma plugin that generates code coverage, and the coverage is produced by Istanbul. The package description says it in two words, a Karma plugin, generate code coverage, and the README adds the same thing in a blockquote.

The mechanism is the standard Karma plugin shape, and it is worth being precise about the two roles the plugin fills, because the configuration gives you one name that does both. The preprocessor runs before the tests and rewrites your source files so that every statement is instrumented to count how many times it executes. The reporter runs after the tests and turns those counters into a report.

In the configuration, the preprocessor appears as a name attached to a file pattern, and the reporter appears in the list of reporters. The same word, coverage, appears in both places, and that is the whole trick of how the plugin integrates with Karma.

The installation is a development dependency alongside Karma itself.

```bash
npm install karma karma-coverage --save-dev
```

The package is MIT licensed, published on npm as karma-coverage, and it is maintained under the karma-runner organisation, which is the group that maintains Karma itself. The repository has a lib directory with the plugin implementation, a docs directory whose configuration page is the reference for the options, an examples directory, and a test directory.

It is a small package with a narrow job, and that is the point of it. It does not try to be a coverage tool in general, it is the bridge between Karma's lifecycle and the Istanbul libraries that do the work.

## Which files you instrument decides what the report means

The basic configuration in the README is short, and the comment inside it is the most important line in the whole example.

```js
// karma.conf.js
module.exports = function(config) {
  config.set({
    files: [
      'src/**/*.js',
      'test/**/*.js'
    ],

    // coverage reporter generates the coverage
    reporters: ['progress', 'coverage'],

    preprocessors: {
      // source files, that you wanna generate coverage for
      // do not include tests or libraries
      // (these files will be instrumented by Istanbul)
      'src/**/*.js': ['coverage']
    },

    // optionally, configure the reporter
    coverageReporter: {
      type : 'html',
      dir : 'coverage/'
    }
  });
};
```

The files list includes both the source and the tests, because Karma has to load both. The preprocessors list includes only the source pattern. That distinction is the whole point, and the comment spells it out: do not include tests or libraries, because those files will be instrumented by Istanbul.

If you instrument the tests, the report becomes a measurement of your test suite rather than of your code. If you instrument the libraries, the report is dominated by code you do not own and cannot fix, and the interesting numbers drown. The coverageReporter block at the bottom is optional, and the default output is an HTML report in a directory called coverage.

The other thing to notice is that the reporter list keeps the progress reporter alongside the coverage one. Karma's progress reporter is the console output that tells you a test run is happening, and dropping it in favour of coverage alone is a common way to make a slow test suite feel like it has hung.

## Several report formats from one run

The advanced example is the one worth copying if you publish coverage anywhere, because it is the difference between a report a human reads and a report a tool consumes.

```js
// karma.conf.js
module.exports = function(config) {
  config.set({
    files: [
      'src/**/*.js',
      'test/**/*.js'
    ],
    reporters: ['progress', 'coverage'],
    preprocessors: {
      'src/**/*.js': ['coverage']
    },
    coverageReporter: {
      // specify a common output directory
      dir: 'build/reports/coverage',
      reporters: [
        // reporters not supporting the `file` property
        { type: 'html', subdir: 'report-html' },
        { type: 'lcov', subdir: 'report-lcov' },
        // reporters supporting the `file` property, use `subdir` to directly
        // output them in the `dir` directory
        { type: 'cobertura', subdir: '.', file: 'cobertura.txt' },
        { type: 'lcovonly', subdir: '.', file: 'report-lcovonly.txt' },
        { type: 'teamcity', subdir: '.', file: 'teamcity.txt' },
        { type: 'text', subdir: '.', file: 'text.txt' },
        { type: 'text-summary', subdir: '.', file: 'text-summary.txt' },
      ]
    }
  });
};
```

The structure is two groups. The first group is the reporters that do not support a file property, the HTML report and the lcov one, and they are given a subdirectory so they do not overwrite each other. The second group is the reporters that do support a file property, and each of them is given a subdirectory of a dot, meaning the output directory itself, plus a file name, so the cobertura report lands as cobertura.txt and the lcov only report as report-lcovonly.txt in the same directory.

The set of formats is the standard Istanbul set, and each one exists because something downstream consumes it. The cobertura XML is what a lot of continuous integration servers read for a coverage badge, the lcov only form is what a coverage service expects, the teamcity format is for the JetBrains continuous integration server, and the text and text summary are for a terminal or a log.

All of them come out of a single test run, which is the point. Running the suite once per format is the mistake this configuration exists to prevent.

The common output directory is set at the top level, so the whole set lands under one path that a build artefact rule can collect.

## CoffeeScript, RequireJS, and the one option that needs setting

The plugin has specific support for CoffeeScript, and the README handles it in three lines with a pointer to the example directories.

For an example of how to use it with CoffeeScript, see the examples/coffee directory. For an example of how to use it with CoffeeScript and the RequireJS module loader, see the examples/coffee-requirejs directory, and also see the useJSExtensionForCoffeeScript option in the configuration documentation.

Two details are hidden in that. The first is that the example project needs a separate preprocessor for the CoffeeScript itself, since Karma has to compile the CoffeeScript to JavaScript before the coverage preprocessor can instrument the result. The dev dependency list confirms this, with a coffee preprocessor package and a RequireJS-capable launcher in the test set.

The second is the useJSExtensionForCoffeeScript option, which is a configuration flag you have to turn on for the combination to work. That is the kind of detail that costs an hour when it is missing, and it is exactly why the README links the option rather than leaving it implicit.

The examples directory in the repository has both of these projects, which is the fastest way to see a working configuration for a CoffeeScript codebase rather than reading the option documentation and guessing.

For a plain JavaScript project, none of this applies, and the basic configuration is the whole story.

## The instrumenter compacts your code, and that is the FAQ

The FAQ in the README has one entry, and it is a good one, because it describes a failure mode that is genuinely confusing the first time you hit it.

The entry is titled Do not minify instrumenter output. When using the Istanbul instrumenter, which is the default, you can disable code compaction by adding a setting to your configuration.

```js
// karma.conf.js
module.exports = function(config) {
  config.set({
    coverageReporter: {
      instrumenterOptions: {
        istanbul: { noCompact: true }
      }
    }
  });
};
```

What the compaction does is rewrite the instrumented source into a more compact form, and the problem is not the size, it is the readability. When a test fails, Karma prints a stack trace pointing into the instrumented file, and if the instrumenter has compacted the file into a single dense line, the line and column numbers in that trace mean nothing to a human looking at the source.

So the trade is between a smaller instrumented file and a stack trace you can read. Turning compaction off makes the instrumented output bigger and the trace usable, and the cost is paid in the browser's memory rather than in your debugging time.

This is a small option with a large effect on how painful a failing test is, and it is the kind of thing that belongs in the basic configuration of every project rather than in a FAQ that most people never read.

## A small plugin with an old release line

The maintenance picture is worth understanding before you depend on this.

The repository is not archived and the last push was on 2026-06-20, so there has been recent activity. The published releases, though, are older: v2.2.1 from 2023-06-23, v2.2.0 from 2022-02-10, and v2.1.1 from 2022-02-05.

The package metadata in the repository shows the version as 2.0.1, which is behind the published tags, and the version in the manifest is the one that matters for what gets installed, so the tags and the manifest are not in step. The release process explains how that happens: the release script runs a contributors update through a grunt task and then semantic-release, and the package version is moved by the release tooling rather than by hand.

The build tooling in the manifest is a period piece, and it tells you the era the project comes from. The linter is ESLint 6 with the standard config, the test runner is Mocha with Chai for assertions, the test harness is Karma 4 with the Mocha launcher, and the build tasks are Grunt with the Karma plugin. The release commits are checked with commitlint using the conventional config, and the semantic-release plugins for the changelog, the git commits, and the npm publish are all present.

The runtime dependencies are the Istanbul libraries, which is the other thing worth noting. The coverage library, the instrument library, the report library, the source maps library, and the reports set, plus minimatch for glob matching. Those are the libraries doing the actual work, and the plugin is the part that wires them into Karma's preprocessor and reporter lifecycle, which is why it has stayed small while the things it depends on have moved on.

For a team that is still on Karma, that is a perfectly good position. For a team that is not, the plugin is a reason to stay, or a reason to migrate.

## Conclusion

karma-coverage fits a team that runs its browser tests under Karma and wants coverage numbers from the same run, with the source maps and multiple report formats the Istanbul libraries provide. It does not fit a project that has moved to another test runner, since it is a Karma plugin and nothing more, and it does not fit a build where the instrumented output has to be minified. Verify first that the files you list in the preprocessors are the source files and not the tests or the libraries, because instrumenting those produces a report that says nothing useful, and that you have read the note about the instrumenter output being compacted, since the option to disable it is the difference between a readable stack trace and an unreadable one.

## FAQ

### What does karma-coverage do?

It is a Karma plugin that generates code coverage using Istanbul. It works as both a preprocessor, which instruments your source files before the tests run, and a reporter, which writes the report when they finish.

### How do I install karma-coverage?

As a development dependency alongside Karma itself, with npm install karma karma-coverage --save-dev. The configuration details are in the docs/configuration page in the repository.

### Which files should I instrument for coverage?

Only your source files. The example configuration lists the source and the test files in the files array, but only the source pattern appears in the preprocessors, and the comment is explicit: do not include tests or libraries, because those files will be instrumented by Istanbul and the report stops describing your code.

### How do I produce several coverage report formats from one test run?

Give the coverageReporter a common directory and a list of reporters. The formats that do not support a file property get a subdirectory, and the ones that do, such as cobertura, lcovonly, teamcity, text, and text-summary, get a subdirectory of a dot and a file name so they land directly in the directory.

### Why are my coverage stack traces unreadable?

Because the Istanbul instrumenter compacts the instrumented output by default. The FAQ in the repository tells you to disable the compaction with the noCompact option inside the instrumenter options of the coverage reporter, which makes the stack traces point at readable source.

## Sources

- [Issues](https://github.com/karma-runner/karma-coverage/issues)
- [karma-runner/karma-coverage on GitHub](https://github.com/karma-runner/karma-coverage)
- [License: MIT](https://github.com/karma-runner/karma-coverage/blob/master/LICENSE)
- [README](https://github.com/karma-runner/karma-coverage/blob/master/README.md)
- [Releases](https://github.com/karma-runner/karma-coverage/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/karma-runner-karma-coverage
