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

Seed-uri Prisma în CI: Cum obții date reproducibile cu Faker și Upsert

De Delia Petre, 4 aug. 2026 · 8 vizualizări · 2 like-uri

Postat 4 aug. 2026
typescript
import { PrismaClient } from '@prisma/client';
import { faker } from '@faker-js/faker';

const prisma = new PrismaClient();

async function main() {
  // Setăm un seed fix pentru determinism în CI
  faker.seed(1337);

  const mockUsers = Array.from({ length: 10 }).map(() => ({
    email: faker.internet.email().toLowerCase(),
    name: faker.person.fullName(),
  }));

  for (const user of mockUsers) {
    await prisma.user.upsert({
      where: { email: user.email },
      update: { name: user.name },
      create: {
        email: user.email,
        name: user.name,
        role: 'USER',
      },
    });
  }
}

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

M-am lovit de prea multe ori în CI de teste e2e picate aleatoriu pentru că datele de seed erau generate diferit la fiecare run. În articolul ăsta îți arăt cum am structurat scriptul de seed la un proiect cu peste 40 de tabele ca să ruleze idempotent în sub 5 secunde.

De ce pică seed-urile standard în pipeline

Pui un prisma.user.create() rapid pe local și totul pare roz. Dai push, declanșezi workflow-ul pe GitHub Actions sau GitLab CI, iar a doua oară când rulează pe un container persistent sau pe o bază de staging, crapă instant: Unique constraint failed on the fields: (email).

Altă problemă clasică apare când folosești @faker-js/faker fără o configurație statică. Un test Playwright sau Cypress caută utilizatorul Ion_Popescu42 generat marți, dar miercuri librăria scuipă Vasile_Ionescu89. Testul crapă, build-ul e roșu și pierzi două ore căutând bug-uri care nu există în cod.

Seed determinist cu Faker

Soluția e simplă, dar surprinzător de mulți dev-i o omit în CI. Faker are o metodă numită faker.seed(number). Când îi dai un număr fix (de exemplu 1337), librăria va genera exact aceeași secvență de nume, emailuri și UUID-uri la fiecare rulare.

Dacă setezi seed-ul static la începutul scriptului prisma/seed.ts, userul #1 din listă va fi mereu identic. Testele tale e2e devin 100% deterministe și scapi de erorile aleatorii din pipeline.

Idempotență prin Prisma Upsert

Regula mea de aur pentru CI e simplă: scriptul de seed trebuie să poată fi executat de 50 de ori la rând pe aceeași bază de date fără să arunce nicio eroare și fără să duplice înregistrările.

Nu folosi create direct în seed. Folosește upsert. Cu upsert, îi dai o condiție where bazată pe un câmp unic (ex: email sau slug). Dacă găsește rândul, face update (sau poți lăsa obiect gol {} dacă nu vrei să suprascrii modificările), iar dacă nu există, execută create.

Trade-off-uri de performanță

Trebuie să fiu sincer: upsert nu e un glonț de argint. La un proiect cu un catalog de peste 8k de produse de testat pe staging, generarea cu upsert individual într-o buclă for...of a dus durata build-ului la 45 de secunde doar pentru seed.

Trade-off-ul e evident: upsert execută interogări individuale pentru fiecare entitate în parte. Pentru dataset-uri masive de peste 10.000 de rânduri, abordarea asta e lentă. În cazurile alea extreme, prefer să dau un TRUNCATE direct prin SQL brut (prisma.$executeRawUnsafe) și apoi să bag datele rapid cu createMany.

Dar pentru 95% din aplicații și pipeline-uri obișnuite de CI, combinația de faker.seed() + upsert rămâne cea mai curată și sigură metodă.

Voi cum gestionați curățarea bazei în CI? Mergeți pe truncate global înainte de seed sau folosiți tranzacții izolate la nivel de test?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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