/testing-initializer

🚀 Simple and complete bootstrapping testing utilities that encourage efficient testing practices.

Primary LanguageTypeScript

Testing Initializer logo

Testing Initializer

Simple and complete bootstrapping testing utilities that encourage efficient testing practices.

Motivation

When testing front-end applications you often need to initialize components, and create mock data matching your back-end. Instead of having to do repeat this process in every test, this library will provide you with a set of utilities for bootstrapping your tests without boilerplate and facilitate efficient data-driven API mocking.

Packages

  • @testing-initializer/react: Testing bootstrapping utilities for React. This library contains @testing-initializer/data. So, if you are using React, I recommend installing only this package.
  • @testing-initializer/data: Wrapper around @msw/data that allows for typed data modelling and relation on a mocked database.

React Testing Initializer

This package contains all necessary utilities for bootstrapping React components in your tests.

Installation

npm i @testing-initializer/data

or with Yarn:

yarn add @testing-initializer/react

Usage

First, let's create our mock database:

import { createDatabase, generateId } from "@testing-initializer/react"

interface User {
  id: number
  name: string
}

interface ToDo {
  id: number
  name: string
}

interface Project {
  id: number
  name: string
  date: string
  user: User
  toDos: ToDo[]
}

interface APITypes {
  user: User
  toDo: ToDo
  project: Project
}

const db = createDatabase<APITypes>({
  user: {
    id: () => generateId("user-pk"),
    name: () => `User ${generateId("user-name")}`,
  },
  toDo: {
    id: () => generateId("toDo-pk"),
    name: () => `Todo ${generateId("toDo-name")}`,
  },
  project: {
    id: () => generateId("project-pk"),
    date: () => new Date().toISOString(),
    name: () => `Project ${generateId("project-name")}`,
    user: oneOf("user"),
    toDos: manyOf("toDo"),
  },
})

Now, let's say we have the following component:

interface TestComponentProps {
  project: Project
}

const TestComponent = ({ project }: TestComponentProps) => {
  return (
    <div>
      <p>{project.user.name}</p>
      <p>{project.name}</p>
      <div>
        {project.toDos.map(({ id, name }) => (
          <p key={id}>{name}</p>
        ))}
      </div>
    </div>
  )
}

We can now proceed to create a renderer, a reusable function that renders your component with its necessary props:

import { createRenderer, createDatabase, generateId } from "@testing-initializer/react"

...

const renderTestComponent = createRenderer()
    .addData("currentUser", () => db.user.create())
    .addData("toDos", () => [db.toDo.create(), db.toDo.create(), db.toDo.create()])
    .setProps(({ currentUser, toDos }) => ({
      project: db.project.create({ toDos, user: currentUser }) as unknown as Project,
      currentUser,
      toDos,
    }))
    .setComponent(TestComponent)
    .setRenderFunction(render)
    .build()

Notice the data property. These extra properties are useful for getting generated data outside of the component props.

With this renderer, we can render our components consistently within tests:

it("...", () => {
  const { project, currentUser, toDos } = renderTestComponent()
})

We can also render override its data or props:

it("...", () => {
  renderTestComponent({
    data: {
      currentUser: db.user.create({ name: "User override" }),
    },
  })
})

Data Testing Initializer

This package allows you to create a @msw/data type-safe database. It will read nested arrays as a many of relationship and objects as a one of relationship.

Installation

npm i @testing-initializer/data

or with Yarn:

yarn add @testing-initializer/data

Usage

First make you have your types available:

interface User {
  id: number
  name: string
}

interface ToDo {
  id: number
  name: string
}

interface Project {
  id: number
  name: string
  date: string
  user: User
  toDos: ToDo[]
}

interface APITypes {
  user: User
  toDo: ToDo
  project: Project
}

Proceed to create your mock database based on this types.

import { createDatabase, generateId } from "@testing-initializer/data"

const db = createDatabase<APITypes>({
  user: {
    id: () => generateId("user-pk"),
    name: () => `User ${generateId("user-name")}`,
  },
  toDo: {
    id: () => generateId("toDo-pk"),
    name: () => `Todo ${generateId("toDo-name")}`,
  },
  project: {
    id: () => generateId("project-pk"),
    date: () => new Date().toISOString(),
    name: () => `Project ${generateId("project-name")}`,
    user: oneOf("user"),
    toDos: manyOf("toDo"),
  },
})

Now you can use all database methods available in @msw/data:

db.user.create()
db.user.findFirst({ ... })
db.project.findMany({ ... })