Modern codebases rarely use plain global scripts — they use ES modules, TypeScript, and bundler-specific features. This lesson covers karma-webpack, the standard way to bundle that code before Karma serves it to the browser.
Why Karma Needs a Bundler for Modern Code
Karma's files array simply injects <script> tags — it has no built-in understanding of import/export syntax, TypeScript, or module resolution. karma-webpack solves this by registering as both a framework and a preprocessor: it runs your webpack configuration against matched files, producing browser-ready bundles Karma can serve normally.
devtool: 'inline-source-map' keeps stack traces readable in test failures.
This webpack config is entirely separate from any production build config, though they often share loader rules.
karma-webpack runs an in-memory webpack build per test run rather than writing bundles to disk.
Watch mode reuses webpack's incremental compilation, keeping reruns fast after the first build.
karma-webpack Cheat Sheet
Key configuration points to remember when wiring up karma-webpack.
Setting
Purpose
frameworks: [..., 'webpack']
Registers the webpack integration
preprocessors: { pattern: ['webpack'] }
Applies bundling to matched files
config.webpack
The webpack configuration object karma-webpack uses
config.webpackMiddleware
Optional dev-middleware options (e.g. verbosity)
devtool: 'inline-source-map'
Keeps stack traces mapped to original source
How karma-webpack Handles Entry Points
Unlike a normal webpack build with a fixed entry, karma-webpack treats every file matched by the associated preprocessors pattern as its own entry point, bundling each spec file (and its imports) independently for the browser to load.
Each matched spec file becomes an implicit webpack entry point.
Shared imports (like a utility module used by many specs) get resolved and bundled per entry.
This can make full rebuilds slower on very large suites — incremental watch-mode rebuilds mitigate this significantly.
output settings in a normal webpack config are ignored; karma-webpack manages bundling internally.
Combining karma-webpack with Coverage
When using webpack alongside karma-coverage, instrumentation typically happens via a babel/webpack loader (like babel-plugin-istanbul) rather than Karma's standalone coverage preprocessor, since the code has already been transformed by webpack before Karma would otherwise instrument it.
Listing webpack in preprocessors but forgetting to also list it in frameworks, or vice versa.
Reusing a production webpack config unmodified, pulling in optimizations (like minification) that make failures harder to read.
Applying Karma's standalone karma-coverage preprocessor on top of already-webpack-transformed code, producing incorrect coverage mappings.
Forgetting devtool: 'inline-source-map', making stack traces point at bundled rather than original source lines.
Key Takeaways
karma-webpack bundles modern module-based code so Karma can serve and run it in the browser.
It requires registration in both frameworks and a matching preprocessors entry.
Each matched spec file becomes an implicit webpack entry point, bundled independently.
Coverage instrumentation with webpack typically happens via a loader plugin, not Karma's standalone coverage preprocessor.
Pro Tip
Keep your webpack test config as close to your production config's module resolution rules as possible (aliases, extensions), but strip anything related to optimization or minification. Divergence here is a frequent, hard-to-spot source of 'works in the app but not in tests' bugs.
You now understand karma-webpack. Next, look at karma-typescript, an alternative approach for TypeScript-heavy projects.