Skip to content

πŸš€ Simple and complete bootstrapping testing utilities that encourage efficient testing practices.

Notifications You must be signed in to change notification settings

JoseRFelix/testing-initializer

Repository files navigation

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({ ... })

About

πŸš€ Simple and complete bootstrapping testing utilities that encourage efficient testing practices.

Resources

Stars

Watchers

Forks

Packages

No packages published