Supplementary tools

Published: 2022-11-30 by Lars  tooltestcode

This is part 2 of Build your own test runner.

All of the remaining features that we often find in popular test runners, but were left out of our minimalistic test runner in part 1, can be provided by separate complementary tools and libraries. Those features therefore doesn't really have to be part of the test runner as such, and in this part we briefly show how each of those features can be provided. For each feature, we provide links to working demos in the repo https://github.com/larsthorup/testrunner/.

Reporting results

Instead of including various reporters in our test runner, we can provide reporting modules as plugins.

Working demo here: @larsthorup/console-reporter-plugin.

Finding test files

It is cumbersome to list the names of each test file on the command line, but instead of adding support for finding test files to our test runner, we can use Unix commands like ls and xargs to search for test files and pass the names to the test runner, like this:

ls -1 src/*.test.js | xargs testrunner

For more advanced "glob" searches, we can use the glob package.

Working demo here: @larsthorup/testfinder.

Writing assertions

Instead of adding support for specialized assertions to our test runner, some assertion libraries, like Chai, allows developers to plugin additional assertions.

Working demo here: @larsthorup/chai-jest-matchers.

Expecting a test to fail

Instead of adding special syntax to our test runner to expect a test to fail (like it.fails), we can use a function to "invert" the test result.

Working demo here: @larsthorup/testutils.

Skipping a test

Instead of adding special syntax to our test runner to skip a test (like it.skip), we can write plugins to add generic "options" (like {skip: true}) to it and describe.

Working demo here: @larsthorup/skip-testrunner-plugin.

Repeating a test

Instead of adding special syntax to our test runner to repeat a test (like it.each), we can simply use the built-in JavaScript map function.

Working demo here: Array.map.

Timing out tests

Instead of adding special syntax to our test runner to timeout a long running test, we can use a function to fail the test if it takes too long.

Working demo here: @larsthorup/testutils.

Having tests in source files

Instead of adding special syntax to our test runner to allow tests to live inside the source files of the code under test, we can set an environment variable IS_TEST, and have the test runner also process sources files.

Working demo here: in-source/lib.js.

Isolating tests from each other

Instead of adding special support for isolating tests from each other (e.g. global variables), we can use the Node.js node:vm module to run specific tests in isolation.

No demo yet...

Mocking timers

Instead of adding special syntax to our test runner for mocking timers, we can use popular mocking libraries like Sinon.

Working demo here: @sinonjs/fake-timers.

Mocking objects

Instead of adding special syntax to our test runner for mocking objects, we can use modern object mocking libraries like tinyspy.

Working demo here: tinyspy.

Mocking modules

Instead of adding special syntax to our test runner for mocking ES modules, we can use modern module mocking libraries like esmock.

Working demo here: esmock.

Emulating a browser

Instead of adding support for DOM emulation to our test runner, we can use popular libraries like "jsdom" or happy-dom which works well with DOM assertions of testing-library.

Working demo here: happy-dom.

Reporting coverage

Instead of adding support for code coverage reporting to our test runner, we can use popular tools like c8.

Working demo here: c8.

Transpiling

Instead of adding support for TypeScript or JSX or any other transpilable language to our test runner, we can run the transpilation, before running tests.

Working demo here: tsc.

Watching

Instead of adding support for "watching" to our test runner, we can watch for changes and only run the subset of tests affected (directly or indirectly) by those changes by recording dependencies in an ESM loader.

Working demo here: @larsthorup/esm-tracer.

Debugging

Instead of adding support for debugging tests to our test runner, we can simply use the built-in JavaScript debugger; statement and run the test runner under a debugger, like ndb.

Working demo here: ndb.

Discuss on Twitter