eduardweb.
Prisma ORMIntermediar#typescript#prisma#ci-cd#databases#testing

Seed-uri Prisma în CI: cum scapi de teste picate aiurea și date flaky

De Ana Ionescu, 29 sept. 2026 · 9 vizualizări · 2 like-uri

Postat acum 2 zile
typescript
import { PrismaClient } from '@prisma/client';
import { faker } from '@faker-js/faker';

const prisma = new PrismaClient();

async function main() {
  // Forțăm faker să fie determinist
  faker.seed(123456);

  // 1. Fixture fix pentru autentificare în teste
  const testAdmin = await prisma.user.upsert({
    where: { email: 'ci-admin@test.local' },
    update: {},
    create: {
      id: 'usr_ci_admin_fixed_id',
      email: 'ci-admin@test.local',
      role: 'ADMIN',
      name: 'CI Automation Admin',
    },
  });

  // 2. Date dinamice, dar perfect reproductibile
  const dummyUsers = Array.from({ length: 5 }).map(() => ({
    email: faker.internet.email().toLowerCase(),
    name: faker.person.fullName(),
  }));

  await prisma.user.createMany({
    data: dummyUsers,
    skipDuplicates: true,
  });
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

Dacă ți-a picat vreodată un pipeline în CI din senin pentru că seed-ul a generat un email duplicat sau un string prea lung pe un câmp de test, știi exact despre ce vorbesc. Am pățit-o acum vreo doi ani pe un proiect cu 14 servicii și vreo 40 de teste E2E: o dată la trei zile cineva dădea re-run la un job doar ca să treacă seed-ul.

Seed-ul de testare nu este același lucru cu seed-ul de demo. În CI vrei două lucruri simple: viteză și determinism absolut. Dacă rulezi pipeline-ul de 100 de ori, baza de date trebuie să arate identic la fiecare rulare.

Problema cu faker lăsat liber

Mulți aruncă @faker-js/faker în prisma/seed.ts și îl lasă să genereze date complet random. Pare o idee bună până când un string random conține un caracter ciudat care îți crapă un regex dintr-un test de frontend, sau până când faker generează din greșeală un status pe care testul tău nu îl aștepta.

Soluția e ridicol de simplă, dar surprinzător de mulți o omit: setează un seed static. Apelul faker.seed(42) la începutul fișierului forțează biblioteca să genereze aceeași secvență de date pseudo-aleatoare de fiecare dată. Ai diversitate vizuală în date, dar zero surprize între două commit-uri diferite.

Upsert în loc de create sau truncate agresiv

Cea mai mare greșeală pe care am văzut-o este prisma.user.deleteMany() urmat de prisma.user.create(). În Postgres, dacă ai tabele legate cu foreign keys complexe sau enum-uri ciudate, un truncate sau delete în lanț devine lent și fragil pe CI când rulezi joburi în paralel pe baze diferite.

Strategia mea preferată este să împart seed-ul în două straturi:

  1. Core fixtures: Date fixe de care depind testele direct (un cont de admin, două companii, un user standard). Aici folosesc exclusiv prisma.user.upsert. Fiecare are un ID fix sau un email hardcodat. Dacă seed-ul rulează a doua oară peste o bază semi-populată, nu crapă.
  2. Noise data: Dacă ai nevoie de 50 de comenzi ca să testezi o paginare, folosești createMany după ce ai setat faker.seed(). Datele astea nu au ID-uri hardcodate, dar vor fi identice între rulări datorită seed-ului matematic.

Trade-off-ul e destul de clar: un seed scris cu upsert e ceva mai lent la execuție decât un TRUNCATE CASCADE urmat de raw insert. La noi, pe o suită de 42 de teste de integrare, scrierea cu upsert adaugă cam 3 secunde la pasul de pregătire. În schimb, am eliminat complet acele 3-4 rulări picate săptămânal din cauza cheilor unice.

Cum rulezi curat în pipeline

În GitHub Actions sau GitLab CI, comanda pe care o vrei nu este npx prisma db push urmat de manual run, ci workflow-ul nativ de migrare. Înainte de suita de teste, rulăm prisma migrate reset --force --skip-seed, urmat manual de rularea scriptului nostru de seed specific pentru mediul de test.

Făcând asta separat, putem injecta variabile de mediu precum TEST_SEED=true, care îi spun scriptului să nu ruleze datele grele de demo destinate dev-ului local, ci doar fixture-urile minimale necesare suitei de testare.

Voi cum gestionați datele de seed în testele de integrare? Mergeți pe mock-uri de bază sau ridicați containere efemere cu Postgres și date reale?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.