Karma calls itself 'a simple tool that lets you spawn browsers and run JavaScript code against them.' This lesson breaks down exactly what happens between running karma start and seeing results in your terminal.
The Karma Server Process
When you run karma start, Karma boots a small local web server (port 9876 by default). This server generates an HTML context page on the fly, containing <script> tags for every file listed in your files array, plus a client-side script that connects back to the server over Socket.IO.
Karma then uses the launcher plugin(s) you configured (like karma-chrome-launcher) to open a browser process pointed at that local server URL. The browser loads the generated page, executes your framework adapter and test files, and the client script streams every test result back to the server in real time.
// Simplified view of what Karma generates and serves
http://localhost:9876/context.html
<script src="/base/src/sum.js"></script>
<script src="/base/src/sum.spec.js"></script>
<script src="karma.js"></script> <!-- connects back via Socket.IO -->
You never write this HTML yourself — Karma generates it dynamically based on your files configuration.
The Full Test Run Lifecycle
1. karma start reads karma.conf.js
2. Karma server starts, listening on config.port (default 9876)
3. Karma launches each configured browser via its launcher plugin
4. Each browser requests the generated context page
5. Framework adapter (karma-jasmine/karma-mocha) executes specs
6. Results stream back over Socket.IO as each test finishes
7. Reporters format and print results as they arrive
8. If singleRun: true, Karma kills the browser(s) and exits with a code
9. If singleRun: false, Karma stays open, watching files for changes
Karma communicates with browsers over a real network socket, not by shelling out and parsing stdout.
Multiple browsers run the exact same generated page independently and in parallel.
The exit code from a singleRun execution is what CI systems use to decide pass/fail.
The server keeps running between file changes in watch mode, so reruns are faster than a fresh karma start.
Test Runner Cheat Sheet
Key facts about how Karma executes a run, condensed for quick reference.
Piece
Role
Karma server
Serves the generated HTML context page and files
config.port
Port the local server listens on (default 9876)
Launcher plugin
Starts/stops a specific browser process
Socket.IO channel
Streams results from the browser back to the server
Framework adapter
Runs the actual describe/it blocks inside the browser
Reporter
Formats streamed results for humans or CI tools
Exit code
0 on all-pass, non-zero on any failure (with singleRun)
The Context Page and /base/ URLs
You'll often see /base/ in Karma's browser console or error stack traces — this is the URL prefix Karma uses to serve every file listed in files, resolved relative to basePath. Understanding this prefix helps when debugging why a file 404s inside the browser but exists on disk.
// karma.conf.js
config.set({
basePath: '',
files: ['src/**/*.spec.js']
});
// A matched file like src/app/math.spec.js becomes reachable at:
// http://localhost:9876/base/src/app/math.spec.js
Running Multiple Browsers at Once
Because each browser connects to the same Karma server independently, listing several browsers runs your entire suite in each one, in parallel, and Karma aggregates the pass/fail totals per browser in its final summary.
browsers: ['ChromeHeadless', 'FirefoxHeadless'] runs the suite twice, once per engine.
A failure specific to one browser (e.g. a WebKit quirk) shows up clearly, isolated by browser name.
Running many browsers increases total wall-clock time and machine resource usage.
CI pipelines often split browsers across separate jobs instead of running them all in one process.
Common Mistakes
Assuming Karma parses console output from a headless process instead of using a real network socket connection.
Hardcoding localhost:9876 URLs in application code, which breaks if config.port is changed.
Not realizing watch mode keeps the server (and browser) alive between runs, which can mask stale-state bugs.
Debugging a 404 for a spec file without checking whether it actually matches a files glob pattern.
Key Takeaways
Karma runs a local server that generates an HTML page listing your files as script tags.
Launcher plugins start real or headless browsers pointed at that server.
Results stream back over a Socket.IO connection in real time as each test finishes.
singleRun controls whether Karma exits with a code (CI) or stays open watching files (local dev).
Pro Tip
Open http://localhost:9876/debug.html in a real (non-headless) browser while Karma is running in watch mode. It's the same context page Karma generates internally, and it's the single best way to use real browser devtools against your test files.
You now understand Karma's runner architecture end to end. Next, dive deep into karma.conf.js, the file that drives all of it.