Test'em 'Scripts! A test runner that makes Javascript unit testing fun.
Test'em 'Scripts! A test runner that makes Javascript unit testing fun.
Testem is a JavaScript test runner that runs your tests in real desktop browsers—Chrome, Firefox, Safari, Edge, and others you launch—so your specs execute in the same browser engines and DOM your users get, not a pretend environment. It also runs tests in Node, Chrome (including headless runs via browser_args, e.g. --headless), or any launcher you configure. It is framework-agnostic and aimed at any kind of tests you want to run: unit, integration, end-to-end style suites, or custom setups—you pick the style; Testem wires it to the browser or process.
Unit testing in JavaScript can be tedious and painful, but Testem makes it so easy that you will actually want to write tests.
browser_args with --headless for headless runs—see docs/browser_args.md)Testem needs a supported Node.js runtime. The required range is defined in package.json under engines (currently ^20.19.0, ^22.12.0, ^24.0.0, or >= 26.0.0).
Recommended: install Testem both as a dev dependency (so your project pins a version) and globally (so the testem command is always available on your PATH):
npm install testem --save-dev
npm install testem -gWhen you run the global testem inside a project directory that has a local testem install, the CLI automatically re-runs the local copy so the version stays in sync with package.json. Set TESTEM_USE_GLOBAL=1 if you ever need to force the global binary only.
This README uses the testem command in examples; add it to npm scripts or invoke it as testem from a shell after the installs above.
As stated before, Testem supports two use cases: test-driven-development and continuous integration. Let's go over each one.
The simplest way to use Testem, in the TDD spirit, is to start in an empty directory and run the command
testemYou will see a terminal-based interface which looks like this
Now open a real browser (the URL Testem prints is a normal page in Chrome, Firefox, Safari, etc.) and go to the specified URL. You should now see
We see 0/0 for tests because at this point we haven't written any code. As we write them, Testem will pick up any .js files
that were added, include them, and if there are tests, run them automatically. So let's first write hello_spec.js in the spirit of "test first" (written in Jasmine)
describe('hello', function(){
it('should say hello', function(){
expect(hello()).toBe('hello world');
});
});Save that file and now you should see
Testem should automatically pick up the new files you've added and also any changes that you make to them and rerun the tests. The test fails as we'd expect. Now we implement the spec like so in hello.js
function hello(){
return "hello world";
}So you should now see
In development mode, Testem has a text-based graphical user interface which uses keyboard-based controls. Here is a list of the control keys
In development mode, Testem watches your project directory for changes and re-runs tests when a relevant file is added, edited, or removed. Watching is implemented with chokidar (v5).
src_files — Glob patterns for source files whose changes should trigger a run (defaults to
*.js when unset). This is the main watch list.watch_files — Optional; if set, these patterns are watched instead of defaulting to
src_files (see docs/config_file.md).src_files_ignore — Patterns to exclude from the watch policy (e.g. node_modules).disable_watching — Set to true to turn off the file watcher entirely.Testem watches the current working directory and applies your include/ignore patterns to events from the watcher. You do not need to list every file explicitly; globs and ignores follow the same policy as in the config reference.
Troubleshooting: On some setups (Docker, network filesystems, VMs), native fs.watch can be
flaky. Chokidar supports environment variables such as CHOKIDAR_USE_POLLING=1 (force polling)
and CHOKIDAR_INTERVAL (polling interval in ms). See the
chokidar readme for details.
To see all command line options
testem --helpTo use Testem for continuous integration
testem ciGitHub Actions is a common way to run Testem in CI: add a workflow job that runs testem ci (often with the Headless Chrome or Chromium launcher). This project’s own workflow is in .github/workflows/ci.yml.
In CI mode, Testem runs your tests on all the browsers that are available on the system one after another.
You can run multiple browsers in parallel in CI mode by specifying the --parallel (or -P) option to be the number of concurrent running browsers.
testem ci -P 5 # run 5 browser in parallelTo find out what browsers are currently available - those that Testem knows about and can make use of
testem launchersWill print them out. The output might look like
$ testem launchers
Browsers available on this system:
IE11
Chrome
Firefox
Safari
Safari Technology Preview
OperaYour machine may list other launchers too. For headless runs, prefer Chrome with browser_args (for example --headless) rather than the deprecated PhantomJS launcher—see docs/browser_args.md.
When you run testem ci to run tests, it outputs the results in the TAP format by default, which looks like
ok 1 Chrome 130.0 - hello should say hello.
1..1
# tests 1
# pass 1
# okTAP is a human-readable and language-agnostic test result format. On GitHub Actions, a typical pattern is a step that runs testem ci and relies on the exit code to fail the job (see .github/workflows/ci.yml in this repository). For Jenkins and TeamCity, use TAP plugins:
By default, the TAP reporter outputs all test results to the console, whether pass or fail. You can disable this behavior in order to make it easier to see which tests fail (i.e. only output failing tests) using:
{
"tap_failed_tests_only": true
}By default, the TAP reporter outputs console logs (distinct from pass/fail information) from all tests that emit logs to the console. You can disable this behavior and only emit logs for failed tests using:
{
"tap_quiet_logs": true
}For improved ergonomics, TAP reporter does not actually strictly adhere to the SPEC by default, reporting 'skip' as a possible status instead of as a directive. To strictly follow the spec use:
{
"tap_strict_spec_compliance": true
}By default, the TAP reporter outputs the result of JSON.stringify() for any log content that is not a String. You can override this behavior by specifying a function for tap_log_processor.
{
"tap_log_processor": function(log) { return log.toString(); }
}Testem has other test reporters besides TAP: dot, xunit and teamcity. You can use the -R to specify them
testem ci -R dotYou can also add your own reporter.
Examplexunit reporter outputNote that the real output is not pretty printed.
…teamcity reporter output…To see all command line options for CI
testem ci --helpFor the simplest JavaScript projects, the TDD workflow described above will work fine. There are times when you want to structure your source files into separate directories, or want to have finer control over what files to include.
This calls for the testem.json configuration file (you can also alternatively use the YAML format with a testem.yml file or return json from a javascript file testem.js). It looks like
{
"framework": "jasmine2",
"src_files": [
"hello.js",
"hello_spec.js"
]
}The default framework is still "jasmine" (Jasmine 1.x). That default is deprecated and will be removed in the next version of Testem. New projects should use "jasmine2" with jasmine-core (see Browser framework dependencies).
The src_files can also be unix glob patterns.
{
"src_files": [
"js/**/*.js",
"spec/**/*.js"
]
}You can also ignore certain files using src_files_ignore.
Update: I've removed the ability to use a space-separated list of globs as a string in the src_files property because it disallowed matching files or directories with spaces in them.
{
"src_files": [
"js/**/*.js",
"spec/**/*.js"
],
"src_files_ignore": "js/toxic/*.js"
}Read more details about the config options.
Built-in mocha, mocha+chai, qunit, and jasmine2 runners prefer files from /node_modules/ in the project cwd. If those packages are not installed, Testem still loads the previous CDN URLs (Mocha 2.3.4, Chai 3.4.1, QUnit 1.20.0, Jasmine 2.4.1). Installing the packages is optional on this version and required in the next major version.
framework |
Install for modern versions |
|---|---|
jasmine2 |
jasmine-core |
qunit |
qunit |
mocha |
mocha |
mocha+chai |
mocha and chai |
Run npm install in the project directory. In a monorepo, or when cwd is not the install root, map the path with routes:
{
"routes": {
"/node_modules": "../node_modules"
}
}The mocha+chai runner loads local Chai
No open issues yet, or sync has not completed.