Skip to main content

TypeScript support

ยท 4 min read
Nico Jansen
Nico Jansen
Stryker Team

Stryker 0.10 marks the biggest change in Stryker history yet, as we now support running mutation testing on TypeScript code.

For the entire changelog per package:

Language agnosticโ€‹

Stryker is now language agnostic. This means that you can use Stryker to mutation test any language that transpiles down to JavaScript (as long as you have the correct plugin for it). TypeScript is the first language that is fully supported in this way.

To make this possible we made a number of changes to the Stryker core:

  • Changed the Mutator plugin api
  • Add a Transpiler plugin api

Mutator plugin apiโ€‹

With the previous Mutator plugin, it was possible to mutate a single Abstract Syntax Tree (AST) node. It was a neat little feature, but was never used outside of the core Stryker package. This plugin was also specific to JavaScript. As Stryker is now language agnostic, we decided to move the Mutator api to a higher level of abstraction. A Mutator is now responsible for mutating code written in a specific language, instead of a single JS AST node.

We moved the existing es5 Mutator code into a new Mutator called 'es5'. It is at this moment still part of the main Stryker npm module, but we have plans to remove it later on. We also added a 'typescript' mutator as part of the new stryker-typescript npm module.

You can find the new api definition here.

Transpiler plugin apiโ€‹

With a transpiler plugin, it possible to transform any source code before Stryker runs your tests. It is also used to transpile each mutant in the same way. This allows maximum freedom when mutating source code, without having to worry about the resulting JavaScript code. A side effect is that it will not work with coverage analysis yet, meaning that Stryker will force coverage analysis to be 'off' when a transpiler is used.

We created the 'typescript' transpiler as part of the new stryker-typescript npm module.

You can find the new api definition here

The TypeScript mutation testing frameworkโ€‹

Original PR

Stryker now supports TypeScript. This means that Stryker is now able to work directly on your TypeScript code, no need to first transpile it yourself and run Stryker on the transpiled code. This has the following advantages:

  • Far less false-positives,
  • Your stryker reports will now show your code, instead of transpiled code.

"There's a plugin for that"โ€‹

All Stryker plugins to help you with TypeScript are part of a single npm module: stryker-typescript. It contains 3 plugins that work together:

  1. Config Editor: A plugin that reads your tsconfig.json file and makes the config available to both the mutator and transpiler.
  2. Mutator: A plugin that can mutate TypeScript code. See our unit tests to know what mutations are support.
  3. Transpiler: A plugin that can transpile (mutated) TypeScript code. It uses your tsconfig settings, so the output will be exactly as you'd expect.

To configure the plugins you need to add this to your stryker.conf.js file:

module.exports = function (config) {
config.set({
// ...
mutator: 'typescript',
transpilers: ['typescript'],
tsconfigFile: 'tsconfig.json',
// ...
});
};

Great! But what about performance?โ€‹

You might be thinking all this has a negative impact on performance. After all, we have to transpile the TypeScript code for each mutant we want to test. When running Stryker on itself we notice that it takes less time than before when we were mutating JavaScript. This is because transpiling each mutant ensures that no false positives are tested. Results for your project may differ.

Take the BinaryExpressionMutator for example. It might mutate:

'foo' + 'bar';

into:

'foo' - 'bar';

Although this would be valid in JavaScript, it is invalid in TypeScript. This means that the mutant will result in a TranspileError. It is not tested and not calculated into your mutation score. Less work means it can be faster.

What's next?โ€‹

Next, we want to further improve upon our TypeScript support. For example we want to add support for coverage analysis when using a transpiler. We're also thinking about an es6 mutator using Babel under the hood.

Please let us know what you think.