Skip to content

A support library to facilitate the easy creation of test data builders for use with an Object-Mother test pattern in TypeScript

License

Notifications You must be signed in to change notification settings

MakerXStudio/ts-dossier

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

98 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TypeScript Dossier (ts-dossier)

A support library to facilitate the easy creation of test data builders for use with an Object-Mother pattern in TypeScript

npm package Build Status Downloads Issues Semantic Release

Install

npm install @makerx/ts-dossier --save-dev

Usage

The first step is to define a model

type Colour = 'Blue' | 'Red' | 'Yellow' | 'Green'

export type Shape = {
  name: string
  sides: number
  sideLengths: number[]
  colour: Colour
}

Then define a Builder for that model

import { randomElement, randomNumberBetween, randomString } from '@makerx/ts-dossier'
import { DataBuilder, dossierProxy } from '@makerx/ts-dossier'
import { Shape } from './shape'

function generateSideLengths(sides: number) {
  return [...Array(sides).keys()].map((_) => randomNumberBetween(1, 999))
}

class ShapeBuilder extends DataBuilder<Shape> {
  constructor() {
    const sides = randomNumberBetween(1, 4)
    super({
      name: randomString(10, 20),
      sides,
      sideLengths: generateSideLengths(sides),
      colour: randomElement(['Blue', 'Red', 'Yellow', 'Green']),
    })
  }

  public withSides(sides: number) {
    this.with('sides', sides)
    if (this.thing.sideLengths.length != sides) {
      this.with('sideLengths', generateSideLengths(sides))
    }
    return this
  }

  public asSquare(length: number) {
    this.thing = {
      ...this.thing,
      name: 'Square',
      sides: 4,
      sideLengths: [length, length, length, length],
    }
    return this
  }

  public asIsoscelesTriangle(length: number, perimeter: number) {
    this.thing = {
      ...this.thing,
      name: 'Isosceles triangle',
      sides: 3,
      sideLengths: [length, length, perimeter - length * 2],
    }
    return this
  }
}

export const shapeBuilder = dossierProxy<ShapeBuilder, Shape>(ShapeBuilder)

With the builder defined, and using the dossier proxy, you now have access to the builder methods supplied by the builder itself and the ones defined for you by the Dossier proxy.

Intellisense Example

Then define a mother to build known models for testing

import { shapeBuilder } from './shape-builder'

export const shapeMother = {
  blueSquare: () => {
    return shapeBuilder().asSquare(20).withColour('Blue')
  },
  greenTriangle: () => {
    return shapeBuilder().withName('Triangle').withSides(3).withColour('Green')
  },
  redIsoscelesTriangle: () => {
    return shapeBuilder().asIsoscelesTriangle(20, 45).withColour('Red')
  },
}

And write some tests

import { describe, expect, it } from '@jest/globals'
import { shapeMother } from './shape-mother'

describe('The square', () => {
  it('has four sides', () => {
    const shape = shapeMother.blueSquare().build()
    expect(shape.sides).toBe(4)
  })
  it('has four sides of equal length', () => {
    const shape = shapeMother.blueSquare().build()
    expect(shape.sideLengths).toEqual(expect.arrayContaining([...Array(4)].map((_) => shape.sideLengths[0])))
  })
  it('is named correctly', () => {
    const shape = shapeMother.blueSquare().build()
    expect(shape.name).toBe('Square')
  })
  it('is coloured blue', () => {
    const shape = shapeMother.blueSquare().build()
    expect(shape.colour).toBe('Blue')
  })
})
describe('The isosceles triangle', () => {
  it('has three sides', () => {
    const shape = shapeMother.redIsoscelesTriangle().build()
    expect(shape.sides).toBe(3)
  })
  it('has two sides of equal length', () => {
    const shape = shapeMother.redIsoscelesTriangle().build()
    expect(shape.sideLengths.reduce<number[]>((a, c) => (a.includes(c) ? a : [...a, c]), [])).toHaveLength(2)
  })
  it('is named correctly', () => {
    const shape = shapeMother.redIsoscelesTriangle().build()
    expect(shape.name).toBe('Isosceles triangle')
  })
  it('is coloured red', () => {
    const shape = shapeMother.redIsoscelesTriangle().build()
    expect(shape.colour).toBe('Red')
  })
})

Try it out on StackBlitz

Random Data Builders

Dossier comes with a variety of random data builders - View detailed function descriptions includes arguments in the code docs.

Name Function Other
Number randomNumber
Number between randomNumberBetween
Float between randomFloatBetween
String randomString
ID randomId
Date randomDate
Date between randomDateBetween
Boolean randomBoolean
Incremented number incrementedNumber Returns a unique incremented number. Call resetIncrementedNumbers to reset
Element from a collection randomElement
Name of a thing randomThingName
Name of a person randomPersonName
Email randomEmail
Phone number randomPhoneNumber
URL randomUrl