Skip to main content

Tests

A Betterer test is a programmatic definition of something that you want to improve in your codebase. Typically this will either be something that is measured across the entire codebase (e.g. code coverage, or number of tests, or a specific performance metric), or something that is measured on a per-file basis (e.g. type checking, or linting).

Tests are defined as code and can be re-used and shared with other projects. Betterer even comes with a few built-in tests.

Basic test#

The most basic test you can write checks if a number grows or shrinks:

// .betterer.tsimport { bigger, smaller } from '@betterer/constraints';
export default {  'should grow': {    test: () => getNumberOfTests(),    constraint: bigger  },  'should shrink': {    test: () => getBundleSize(),    constraint: smaller  }};
function getNumberOfTests(): number {  // ...}
function getBundleSize(): number {  // ...}

Most of the time you should wrap your test in a BettererTest. This adds a few nice helpers like only() and skip().

// .betterer.tsimport { BettererTest } from '@betterer/betterer';import { bigger, smaller } from '@betterer/constraints';
export default {  'should grow': new BettererTest({    test: () => getNumberOfTests(),    constraint: bigger  }).only(),  'should shrink': new BettererTest({    test: () => getBundleSize(),    constraint: smaller  }).skip()};
function getNumberOfTests(): number {  // ...}
function getBundleSize(): number {  // ...}

Test goal#

You can add a goal to your test, which can be a function or a value. Once the goal is met, the test will be marked as "completed":

// .betterer.tsimport { BettererTest } from '@betterer/betterer';import { bigger, smaller } from '@betterer/constraints';
export default {  'should grow': new BettererTest({    test: () => getNumberOfTests(),    constraint: bigger,    goal: (value: number) => value > 1000  }).only(),  'should shrink': new BettererTest({    test: () => getBundleSize(),    constraint: smaller,    goal: 5  }).skip()};
function getNumberOfTests(): number {  // ...}
function getBundleSize(): number {  // ...}

Test deadline#

You can add a deadline to your test, which can be a Date object, or a valid Date string. Once the deadline is passed, the test will be marked as "expired":

// .betterer.tsimport { BettererTest } from '@betterer/betterer';import { bigger, smaller } from '@betterer/constraints';
export default {  'should grow': new BettererTest({    test: () => getNumberOfTests(),    constraint: bigger,    deadline: new Date('2021/07/03')  }),  'should shrink': new BettererTest({    test: () => getBundleSize(),    constraint: smaller,    deadline: '2021/07/03'  })};
function getNumberOfTests(): number {  // ...}
function getBundleSize(): number {  // ...}

File test#

If you want to write a test that checks individual files, you can write a BettererFileTest:

// .betterer.tsimport { BettererFileResolver, BettererFileTest } from '@betterer/betterer';
export default {  'no more JavaScript files': countFiles('no more JavaScript files!').include('**/*.js')};
function countFiles(issue: string) {  const resolver = new BettererFileResolver();  return new BettererFileTest(resolver, async (filePaths, fileTestResult) => {    filePaths.forEach((filePath) => {      // In this case the file contents don't matter:      const file = fileTestResult.addFile(filePath, '');      // Add the issue to the first character of the file:      file.addIssue(0, 0, issue);    });  });}

Complex test#

If you want to do more fancy custom things, you can have complete control over constraints, diffing, serialising/deserialising and printing.

// .betterer.tsimport { BettererTest } from '@betterer/betterer';import { BettererConstraintResult } from '@betterer/constraints';
type AccessibilityReport = {  warnings: Array<string>;  errors: Array<string>;};
export default {  'should be accessible': new BettererTest<AccessibilityReport>({    test: accessibilityTest,    constraint: accessibilityConstraint  })};
function accessibilityTest(): AccessibilityReport {  // ...}
function accessibilityConstraint(result: AccessibilityReport, expected: AccessibilityReport): BettererConstraintResult {  if (result.errors > expected.errors || result.warnings > expected.warnings) {    return BettererConstraintResult.worse;  }  if (result.errors < expected.errors || result.warnings < expected.warnings) {    return BettererConstraintResult.better;  }  return BettererConstraintResult.same;}