Logo Light
Published on

Fitnotes (4. díl) - Implementace CRUD testů pro Prisma model

Implementace CRUD testů pro model Workout

V tomto dílu navážu na předchozí článek, ve kterém jsem si vydefinoval testovací scénáře pro Prisma model Workout. V tomto článku ukážu implementaci CRUD testů pomocí frameworku Jest.


1. Vytvoření validního záznamu

Nejprve vytvořím test, který ověří, že dokážeme vložit platný záznam do databáze.

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()
const today = new Date(Date.now())
const workoutDay = new Date('2024-10-11')

describe('Workout model', () => {
  it('should create a valid workout record', async () => {
    const workout = await prisma.workout.create({
      data: {
        date: workoutDay,
        exercise: 'Bench Press',
        category: 'Chest',
        weight: 100,
        weightUnit: 'kgs',
        reps: 10,
      },
    })

    console.log(workout)
    expect(Object.keys(workout).length).toBe(11)
    expect(workout).toHaveProperty('id')
    expect(workout.date).toEqual(workoutDay)
    expect(workout.exercise).toBe('Bench Press')
    expect(workout.category).toBe('Chest')
    expect(workout.weight).toBe(100)
    expect(workout.weightUnit).toBe('kgs')
    expect(workout.reps).toBe(10)
    expect(workout.distance).toBeNull()
    expect(workout.distanceUnit).toBeNull()
    expect(workout.time).toBeNull()
    expect(workout.createdAt.getTime()).toBeGreaterThan(today.getTime())
    expect(workout.createdAt.getTime()).toBeLessThan(today.getTime() + 10 * 1000)
  })
})

Co obsahuje konstanta workout?

workout obsahuje objekt, který Prisma vrátí po úspěšném vložení záznamu do databáze. Funkce prisma.workout.create(...) odešle SQL dotaz do PostgreSQL databáze, vloží data do tabulky Workout a vrátí javascriptový objekt, jehož property pak v testu ověřuju. V našem případě by se mělo vrátit toto (id a createdAt se bude měnit):

      {
        id: 7,
        date: 2024-10-11T00:00:00.000Z,
        exercise: 'Bench Press',
        category: 'Chest',
        weight: 100,
        weightUnit: 'kgs',
        reps: 10,
        distance: null,
        distanceUnit: null,
        time: null,
        createdAt: 2025-07-04T07:33:49.726Z
      }

Pomocí expect(workout).toHaveProperty('id') ověřujeme, že objekt byl skutečně vytvořen a dostal ID. Ostatní expect volání ověřují, že hodnoty byly uloženy správně.


2. Vytvoření neplatného záznamu

Test, který se pokusí vložit záznam s chybějícím polem (např. exercise). Pro úplné pokrytí by bylo třeba provést test pro všechna povinná pole.

it('should fail when required field is missing', async () => {
  await expect(
    prisma.workout.create({
      // @ts-expect-error - testing invalid input (missing exercise)
      data: {
        date: workoutDay,
        category: 'Chest',
        weight: 100,
        weightUnit: 'kgs',
        reps: 10,
      },
    })
  ).rejects.toThrow()
})

Tímto kódem - await expect(...).rejects.toThrow() - ověřím, že asynchronní operace vyhodí výjimku. Abych potlačil chybové hlášení z TypeScriptu, že data neobsahují propertu exercise, použil jsem direktivu // @ts-expect-error - testing invalid input (missing exercise).


3. Platný update záznamu

it('should update a workout record', async () => {
  const created = await prisma.workout.create({
    data: {
      date: new Date(),
      exercise: 'Squat',
      category: 'Legs',
      weight: 100,
      weightUnit: 'kgs',
      reps: 10,
    },
  })

  const updated = await prisma.workout.update({
    where: { id: created.id },
    data: { exercise: 'Front Squat' },
  })

  expect(updated.exercise).toBe('Front Squat')

  const selectCreatedFromDb = await prisma.workout.findMany({
    where: {
      id: created.id,
    },
  })

  expect(selectCreatedFromDb).toHaveLength(1)
  expect(created.id).toEqual(selectCreatedFromDb[0].id)
  expect(created.exercise).not.toEqual(selectCreatedFromDb[0].exercise)
  expect(created.category).toEqual(selectCreatedFromDb[0].category)
  expect(created.weight).toEqual(selectCreatedFromDb[0].weight)
  expect(created.weightUnit).toEqual(selectCreatedFromDb[0].weightUnit)
  expect(created.reps).toEqual(selectCreatedFromDb[0].reps)
  expect(created.distance).toEqual(selectCreatedFromDb[0].distance)
  expect(created.distanceUnit).toEqual(selectCreatedFromDb[0].distanceUnit)
  expect(created.time).toEqual(selectCreatedFromDb[0].time)
  expect(created.createdAt).toEqual(selectCreatedFromDb[0].createdAt)
})

Po provedení updatu si z databáze znovu načítám aktualizovaný záznam, abych měl jistotu, že změna byla uložena, a že ostatní pole zůstala beze změny.


4. Neplatný update záznamu

it('should fail to update non-existent record', async () => {
  await expect(
    prisma.workout.update({
      where: { id: 99999 },
      data: { exercise: 'Something' },
    })
  ).rejects.toThrow()
})

5. Mazání záznamu

it('should delete a workout record', async () => {
  const toDelete = await prisma.workout.create({
    data: {
      date: new Date(),
      exercise: 'Death Diving',
      category: 'Darwin Award Candidates',
    },
  })

  // check that the entry is stored in db
  const selectToDeleteFromDb = await prisma.workout.findMany({
    where: {
      id: toDelete.id,
    },
  })
  expect(selectToDeleteFromDb[0].id).toEqual(toDelete.id)

  const deleted = await prisma.workout.delete({
    where: { id: toDelete.id },
  })

  expect(deleted.exercise).toBe('Death Diving')

  // check that the entry is not in db anymore
  const selectToDeleteFromDbAfter = await prisma.workout.findMany({
    where: {
      id: toDelete.id,
    },
  })

  expect(selectToDeleteFromDbAfter).toHaveLength(0)
})

Co bude dál?

V dalším dílu se podívám na testy filtrování, řazení a agregací.