İçeriğe geç

Test verisi yönetimi: Her testin kendi verisini üretmesi neden zorunlu

Test paketiniz tek başına çalışırken yeşil, CI’da paralel koşarken kırmızıysa sorun genellikle testlerde değil, testlerin paylaştığı veridedir. beforeAll içinde kurulan bir kullanıcı, migration ile yüklenen bir seed kaydı, “zaten var” varsayımıyla kullanılan bir sipariş numarası — bunların hepsi testler arasında görünmez bir bağımlılık yaratır.

Bu yazı tek bir kural etrafında dönüyor: bir testin okuduğu veya değiştirdiği her kaydı o test üretmiş olmalı. Kuralı nasıl uygulayacağınızı, kaçınılmaz istisnalarını ve mevcut bir paketi bu kurala nasıl taşıyacağınızı anlatıyorum.

Sorun paylaşılan test verisinde başlar

Ortak seed verisi kullanan bir paket, testler arasında yazılı olmayan bir sözleşme kurar: “kimse user_42‘nin e-postasını değiştirmeyecek”. Bu sözleşme derleyici tarafından kontrol edilmez, code review’da görünmez ve testler alfabetik sırayla çalıştığı sürece bozulmaz. Sonra biri yeni bir test dosyası ekler, dosya adı sıralamayı değiştirir ve profil güncelleme testi, listeleme testinin beklediği kaydı bozar.

Bu noktada olan şey şudur: listeleme testi kırmızı yanar, hata mesajı “beklenen 3 satır, bulunan 2 satır” der ve ekip bunu ürün hatası sanıp saatlerce arar. Testi tek başına çalıştırınca geçer. Buna “flaky test” deyip retries: 2 eklemek, teşhisi yanlış koymaktır. Flaky test, aynı girdiyle farklı sonuç veren testtir. Burada girdi farklıdır — veritabanının durumu, önceki testin ne yaptığına bağlıdır. Bu bir zamanlama sorunu değil, mimari bir hatadır: testler arasında paylaşılan değiştirilebilir durum var.

Retry eklemek bu durumda işleri kötüleştirir. Yeniden denenen test, ilk denemede kendi bıraktığı artık veriyi bulur ve bu sefer başka bir nedenle düşer. Ya da tesadüfen geçer ve gerçek bir regresyonu maskeler.

Bu ayrımı yapmak için üç kontrol yeter. Testi tek başına çalıştır: geçiyorsa sorun testte değil izolasyonda. Sırayı değiştir (--shuffle veya dosya adını değiştir): sonuç değişiyorsa testler arasında bağımlılık var. Aynı testi arka arkaya iki kez koştur: ikincisi düşüyorsa test kendi bıraktığı artığı buluyor. Üçü de temiz geçiyorsa gerçekten bir zamanlama sorunun olabilir; geçmiyorsa retries eklemek teşhisi geciktirmekten başka bir işe yaramaz.

Test verisi sahipliği kuralı

Kural tek cümle: bir testin okuduğu veya değiştirdiği her kaydı o test üretmiş olmalı.

Bu kuralın üç doğrudan sonucu var:

  1. Testler sırasız çalışabilir. Hiçbir test, başka bir testin bıraktığı duruma bağlı değildir. Dosya adını değiştirmek, yeni test eklemek veya --shuffle ile çalıştırmak sonucu etkilemez.
  2. Testler paralel çalışabilir. İki worker aynı kaydı aynı anda güncelleyemez, çünkü kayıtlar ayrıdır.
  3. Testler tek başına çalışabilir. Bir hatayı ayıklarken tek testi -g ile çalıştırdığınızda, 40 testlik bir setup zincirini yeniden kurmanız gerekmez. Bu, hata ayıklama süresini doğrudan kısaltır.

Kuralın tek meşru istisnası referans verisidir: ülke listesi, para birimleri, KDV oranları, ürün kategorileri, rol tanımları. Bunlar istisnadır çünkü testler bu veriyi yalnızca okur ve hiçbir test onu değiştirmez. Değiştirilmeyen veri, testler arasında bağımlılık yaratmaz.

İstisnanın sınırı da net: bir test referans veriyi değiştirmek zorundaysa (yeni bir para birimi ekleme testi gibi), o test kendi kaydını üretmeli ve silmeli. “Sadece bu bir tane, nasılsa geri alırım” diyerek ortak listeye dokunan test, kuralın dışında değildir — kuralı ihlal eder.

Fixture ile veri üretimi: setup değil, factory

beforeAll bir setup mekanizmasıdır ve setup doğası gereği paylaşılan durum üretir. İhtiyacınız olan şey factory’dir: çağrıldığında yeni bir kayıt üreten, kimin çağırdığını bilen ve testin sonunda ürettiğini toplayan bir fonksiyon.

Playwright’ta bunun doğal karşılığı test-scoped fixture’dır. Fixture bir değer değil, bir fonksiyon döndürür; böylece bir test bir kullanıcı, başka bir test beş kullanıcı üretebilir.

// fixtures/test-data.ts
import { test as base, expect, APIRequestContext } from '@playwright/test';
import { uniqueEmail } from './unique';

export type TestUser = {
  id: string;
  email: string;
  password: string;
};

type Fixtures = {
  api: APIRequestContext;
  createUser: (overrides?: Partial<TestUser>) => Promise<TestUser>;
};

export const test = base.extend<Fixtures>({
  api: async ({ playwright }, use) => {
    const context = await playwright.request.newContext({
      baseURL: process.env.API_URL ?? 'http://localhost:3000',
      extraHTTPHeaders: {
        Authorization: `Bearer ${process.env.ADMIN_TOKEN ?? ''}`,
      },
    });
    await use(context);
    await context.dispose();
  },

  createUser: async ({ api }, use, testInfo) => {
    const createdIds: string[] = [];

    const factory = async (overrides: Partial<TestUser> = {}): Promise<TestUser> => {
      const email = overrides.email ?? uniqueEmail(testInfo);
      const password = overrides.password ?? 'Test-Passw0rd!';

      const response = await api.post('/api/users', {
        data: { email, password, role: 'customer' },
      });
      expect(response.status(), await response.text()).toBe(201);

      const body = await response.json();
      createdIds.push(body.id);
      return { id: body.id, email, password };
    };

    await use(factory);

    for (const id of createdIds.reverse()) {
      await api.delete(`/api/users/${id}`).catch(() => undefined);
    }
  },
});

export { expect };

Testte kullanımı iki satır:

// tests/profile.spec.ts
import { test, expect } from '../fixtures/test-data';

test('kullanıcı görünen adını güncelleyebilir', async ({ createUser, api }) => {
  const user = await createUser();

  const response = await api.patch(`/api/users/${user.id}`, {
    data: { displayName: 'Yeni Ad' },
  });

  expect(response.status()).toBe(200);
  expect((await response.json()).displayName).toBe('Yeni Ad');
});

Bu testin hangi sırada, kaç worker’la veya kaç kez çalıştığı önemsizdir. Kendi kullanıcısını üretir, onu günceller, siler.

Benzersizlik: rastgele değil, deterministik-benzersiz

user${Math.random()}@test.com iki nedenle yanlıştır. Birincisi çakışır — Math.random() kısa string’e kırpıldığında ve paket günde yüzlerce kez koştuğunda unique constraint hatası alırsınız. İkincisi ve daha önemlisi: veritabanında us********@**st.com kaydını gördüğünüzde bu kaydı hangi testin bıraktığını bilemezsiniz.

Benzersizlik hedefi rastgelelik değil, çakışmayan ve izlenebilir kimliktir. Bunun bileşenleri: çalıştırma kimliği (run id), worker index, test dosyası, test adı ve bir sayaç.

// fixtures/unique.ts
import path from 'node:path';
import type { TestInfo } from '@playwright/test';

// CI'da build numarasını ver: TEST_RUN_ID=$GITHUB_RUN_ID
const RUN_ID = process.env.TEST_RUN_ID ?? String(Date.now());

let counter = 0;

function slug(value: string, maxLength: number): string {
  return value
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '')
    .slice(0, maxLength);
}

export function uniqueTag(testInfo: TestInfo): string {
  counter += 1;
  const file = slug(path.basename(testInfo.file).replace(/\.spec\.ts$/, ''), 20);
  const title = slug(testInfo.title, 24);
  return `${RUN_ID}-w${testInfo.workerIndex}-${file}-${title}-${counter}`;
}

export function uniqueEmail(testInfo: TestInfo): string {
  return `qa+${uniqueTag(testInfo)}@example.test`;
}

counter modül kapsamındadır ve her worker kendi süreçinde çalıştığı için worker başına ayrıdır; workerIndex de tag’de bulunduğundan iki worker’ın aynı değeri üretmesi mümkün değildir.

Kazancı şudur: veritabanında qa+77**************************************@*****le.test gördüğünüzde, kaydın hangi çalıştırmada, hangi worker’da, hangi testte üretildiğini sorgulamadan okursunuz. Artık veriyi temizlerken LIKE 'qa+7781234-%' ile tek bir çalıştırmayı hedefleyebilirsiniz.

API üzerinden kurulum, UI üzerinden doğrulama

Ön koşul verisini UI ile kurmak iki maliyet üretir. Birincisi süre: 12 alanlık bir kayıt formunu doldurmak, aynı kaydı oluşturan bir POST çağrısından onlarca kat yavaştır ve bu maliyet her testte tekrarlanır. İkincisi kırılganlık: forma zorunlu bir alan eklendiğinde, o formu test etmeyen 40 test birden düşer. Düşen testlerin hiçbiri kayıt formunu doğrulamıyordu; sadece kullanıcıya ihtiyaçları vardı.

Kural nettir: ön koşul HTTP ile kurulur, UI yalnızca test edilen davranış için kullanılır. Sipariş iptalini test ediyorsanız kullanıcıyı, ürünü ve siparişi API ile yaratın; tarayıcıda yalnızca iptal akışını yürütün. API katmanını ayrı ve bilinçli test etmek gerektiği konusunda API test otomasyonu yazısına bakabilirsiniz — burada API’yi test etmiyoruz, kurulum aracı olarak kullanıyoruz.

// tests/order-cancel.spec.ts
import { test, expect } from '../fixtures/test-data';

test('kullanıcı bekleyen siparişini iptal edebilir', async ({ page, api, createUser }) => {
  // 1) Ön koşul: tamamen API üzerinden
  const user = await createUser();

  const orderResponse = await api.post('/api/orders', {
    data: { userId: user.id, sku: 'SKU-1001', quantity: 1, status: 'pending' },
  });
  expect(orderResponse.status(), await orderResponse.text()).toBe(201);
  const order = await orderResponse.json();

  const tokenResponse = await api.post('/api/auth/token', {
    data: { email: user.email, password: user.password },
  });
  expect(tokenResponse.ok(), await tokenResponse.text()).toBeTruthy();
  const { accessToken } = await tokenResponse.json();

  await page.addInitScript((token: string) => {
    window.localStorage.setItem('access_token', token);
  }, accessToken);

  // 2) Test edilen davranış: yalnızca burada UI
  await page.goto(`/orders/${order.id}`);
  await page.getByRole('button', { name: 'Siparişi iptal et' }).click();
  await page.getByRole('button', { name: 'Onayla' }).click();

  await expect(page.getByTestId('order-status')).toHaveText('İptal edildi');
});

Sipariş oluşturma formu değiştiğinde bu test etkilenmez. Etkilenmesi gereken tek test, o formu doğrulayan testtir. Kurulumu API’ye taşımanın süre etkisi de gözle görülürdür; genel yaklaşımlar için test otomasyonunda performans optimizasyonu yazısına bakın.

Temizlik stratejileri ve hangisini ne zaman seçmeli

Dört strateji var ve seçim ortam tipine bağlı.

1. Test sonu silme. Fixture teardown’ında ürettiğiniz kayıtları API veya SQL ile silersiniz. Maliyeti: her test için ek ağ çağrıları ve silme sırasına dikkat etme zorunluluğu (foreign key). Uyduğu ortam: paylaşılan bir staging veya QA ortamı; yani veritabanını sıfırlama yetkiniz olmayan yerler. Varsayılan tercih budur.

2. Transaction rollback. Test bir transaction içinde çalışır, sonunda rollback edilir. Maliyeti: yalnızca test kodu ile uygulama aynı bağlantıyı paylaşıyorsa mümkündür. Ayrı süreçte çalışan bir HTTP servisi test ederken uygulanamaz. Uyduğu ortam: aynı süreçte çalışan entegrasyon testleri ve repository katmanı testleri. UI otomasyonu için gerçekçi değildir.

3. Namespace / tenant izolasyonu. Her test veya her worker kendi tenant’ında, kendi hesabında, kendi şemasında çalışır. Maliyeti: uygulamanın çok kiracılı olması veya en azından veri kapsamını bir anahtara göre ayırabilmesi gerekir. Uyduğu ortam: SaaS ürünleri ve silme işleminin yasak olduğu sistemler. En dayanıklı seçenektir çünkü temizliğe bağımlı değildir.

4. Periyodik toplu temizlik. Ortamda gece çalışan bir iş, qa+ önekli veya belirli bir yaşın üzerindeki test kayıtlarını siler. Maliyeti: veri gün içinde birikir; benzersiz olmayan veri kullanan testler bu birikmeden etkilenir. Uyduğu ortam: her ortam. Tek başına yeterli değildir ama diğer üçünün tamamlayıcısıdır ve olmadığında ortam zamanla şişer.

Hangi stratejiyi seçerseniz seçin bir kural değişmez: temizlik, testin assert’ine bağlı olmamalıdır. Temizliği testin son satırına await deleteUser(id) şeklinde yazarsanız, assert düştüğü anda o satır hiç çalışmaz ve her başarısız test ortamda çöp bırakır. Temizlik fixture teardown’ında olmalı; teardown, test düşse de çalışır.

Strateji seçimi üç kısıta bakar. Testin veritabanına doğrudan erişimi varsa transaction rollback en ucuzudur; hiçbir şey kalıcı olarak yazılmaz, temizlik bedavaya gelir. Erişim yoksa ve kurulum API üzerinden yapılıyorsa, her testin ürettiği kaydın kimliğini toplayıp sonda silmek gerekir — bu, silme uçlarının var olmasını şart koşar. Kayıt hiç silinemiyorsa (denetim kaydı, muhasebe kısıtı, zorunlu soft delete) silmeye çalışmayı bırak: çakışmayı imkânsız kılan bir ayrım kullan, yani her teste kendi tenant’ını ya da kendi kimlik alanını ver.

Temizlik başarısız olursa ne olur

Teardown da her zaman çalışmaz. Worker timeout’a düşebilir, süreç SIGKILL alabilir, silme endpoint’i 500 dönebilir. Bu yüzden temizlik en iyi çaba (best effort) olarak tasarlanmalıdır: denenir, sonucu rapora yazılır, ama testin sonucunu değiştirmez.

Temizlik hatasının testi kırmaması gerekir çünkü aksi halde yeşil bir testi kırmızıya çevirirsiniz ve ekip raporda gerçek hatayı bulmakta zorlanır. Bunun yerine hata bilgisini test raporuna iliştirin ve süpürücü işin toplamasına bırakın.

// fixtures/cleanup.ts
import type { APIRequestContext, TestInfo } from '@playwright/test';

export async function bestEffortDelete(
  api: APIRequestContext,
  url: string,
  testInfo: TestInfo,
): Promise<void> {
  try {
    const response = await api.delete(url, { timeout: 10_000 });
    if (!response.ok() && response.status() !== 404) {
      await testInfo.attach('cleanup-warning', {
        body: `DELETE ${url} -> ${response.status()}\n${await response.text()}`,
        contentType: 'text/plain',
      });
    }
  } catch (error) {
    await testInfo.attach('cleanup-error', {
      body: `DELETE ${url} -> ${(error as Error).message}`,
      contentType: 'text/plain',
    });
  }
}

Fixture teardown’ında kullanımı:

await use(factory);

for (const id of createdIds.reverse()) {
  await bestEffortDelete(api, `/api/users/${id}`, testInfo);
}

Bunun karşılığında iki şey borçlusunuz: sistem artık veriye dayanıklı olmalı (bir testin bıraktığı kullanıcı başka bir testi etkilememeli — ki benzersiz veri bunu zaten sağlar) ve ortamda zamanlanmış bir süpürücü iş bulunmalı.

Değiştirilemeyen veriyle çalışmak: üretim benzeri ortamlar

Bazı sistemlerde silme yoktur. Muhasebe kayıtları, denetim izi (audit log), ödeme hareketleri, resmî belge numaraları — bunlar tasarım gereği değiştirilemez. Bazen de veri sizin sisteminizde değil, entegre olduğunuz dış bir sistemde üretilir ve orada silme yetkiniz yoktur.

Bu durumda izolasyonu silmeyle sağlayamazsınız; kapsam ayırmayla sağlarsınız. Pratik seçenekler:

  • Test başına ayrı tenant veya organizasyon. Kayıt kalır ama başka hiçbir testin sorgu kapsamına girmez.
  • Test başına ayrı hesap. Muhasebe sistemlerinde tipik çözüm: her test kendi cari hesabını açar, hareketleri o hesaba yazar, bakiyeyi o hesap üzerinden doğrular.
  • Ayrı tarih aralığı. Raporlama testlerinde işe yarar: her test kendi ürettiği kayıtları kendi seçtiği bir tarih penceresine yazar ve raporu o pencereyle filtreler.
  • Ayrı etiket/referans alanı. Kayda externalRef gibi bir alanda uniqueTag yazıp bütün sorguları bu alanla filtrelemek.

Bu yaklaşımın sınırlarını da bilin. Sistem genelinde tekil olan kaynaklar (fatura numarası sayacı, global stok miktarı, gün sonu toplamı) kapsam ayırmayla izole edilemez. Bu tür davranışları test eden senaryolar gerçekten seri çalışmalıdır; Playwright’ta bunları ayrı bir projeye alıp fullyParallel: false ile çalıştırmak, tüm paketi seri çalıştırmaktan iyidir. İkinci sınır: veri birikir. Üretim benzeri ortamda aylar sonra tablo boyutu sorgu performansını etkiler, bu yüzden yaş bazlı bir arşivleme işi yine gerekir.

Test verisi ve paralel çalıştırma ilişkisi

Paralel çalıştırma yeni bir özellik değil, mevcut veri mimarinizin sınavıdır. workers: 1 ile çalışan bir paket veri sahipliği hakkında hiçbir şey kanıtlamaz; sıra sabit olduğu için ihlaller görünmez. Worker sayısını 4’e çıkardığınızda düşen her test, veri sahipliğinin ihlal edildiği yeri işaret eder. Düşen testlerin listesi, yapılacaklar listenizdir.

Burada iki fixture kapsamı arasındaki farkı doğru kurmak gerekir:

  • Test-scoped veri: testin değiştirdiği her şey. Kullanıcılar, siparişler, ürünler. Her test kendi kaydını üretir.
  • Worker-scoped veri: kurulumu pahalı, testin değiştirmediği ve yalnızca okuduğu şeyler. Bir tenant, bir admin oturumu, bir yapılandırma. Worker başına bir kez kurulur, o worker’daki tüm testler paylaşır.

Ayrım basit: testin değiştirdiği hiçbir şey worker-scoped olmamalıdır. Bir test worker-scoped tenant’ın ayarını değiştirirse, aynı worker’daki sonraki testler bozulur — paylaşılan seed verisinin küçük ölçekli hâline geri dönmüş olursunuz.

// fixtures/tenant.ts
import { test as base, expect, request, APIRequestContext } from '@playwright/test';

export type Tenant = { id: string; slug: string; adminToken: string };

type WorkerFixtures = { tenant: Tenant };
type TestFixtures = { tenantApi: APIRequestContext };

export const test = base.extend<TestFixtures, WorkerFixtures>({
  tenant: [
    async ({}, use, workerInfo) => {
      const admin = await request.newContext({
        baseURL: process.env.API_URL ?? 'http://localhost:3000',
        extraHTTPHeaders: { Authorization: `Bearer ${process.env.ADMIN_TOKEN ?? ''}` },
      });

      const runId = process.env.TEST_RUN_ID ?? String(Date.now());
      const slug = `qa-${runId}-w${workerInfo.workerIndex}`;

      const response = await admin.post('/api/tenants', { data: { slug, plan: 'standard' } });
      expect(response.status(), await response.text()).toBe(201);
      const body = await response.json();

      await use({ id: body.id, slug, adminToken: body.adminToken });

      await admin.delete(`/api/tenants/${body.id}`).catch(() => undefined);
      await admin.dispose();
    },
    { scope: 'worker' },
  ],

  tenantApi: async ({ tenant, playwright }, use) => {
    const context = await playwright.request.newContext({
      baseURL: process.env.API_URL ?? 'http://localhost:3000',
      extraHTTPHeaders: {
        Authorization: `Bearer ${tenant.adminToken}`,
        'X-Tenant': tenant.slug,
      },
    });
    await use(context);
    await context.dispose();
  },
});

export { expect };

Bu yapıda tenant kurulumu worker başına bir kez ödenir, testler kendi kayıtlarını o tenant içinde üretir ve iki worker asla aynı veri kümesine bakmaz. Paketi büyütürken kapsam kararlarının nasıl değiştiğine dair daha geniş bir çerçeve için ölçeklenebilirlik yazısına bakabilirsiniz.

Worker sayısını artırırken iki sayı birlikte anlam kazanır: toplam süre ve düşen test sayısı. Süre worker sayısıyla orantılı kısalmıyorsa darboğaz testlerde değil, paylaşılan bir kaynaktadır — çoğunlukla veritabanı bağlantı havuzu ya da worker başına tekrarlanan tek bir kurulum adımı. Düşen test sayısı artıyorsa izolasyon eksiktir; hangi testlerin düştüğüne bak, aynı tabloya dokunanlar bir arada çıkar. İkisini aynı anda değiştirme: önce worker sayısını sabit tutup izolasyonu düzelt, sonra artır. Aksi hâlde hangi değişikliğin neyi düzelttiğini bilemezsin.

Sözleşme ve mock sınırında test verisi

Mocklanan bir bağımlılık için yazdığınız sahte yanıt da test verisidir ve aynı sahiplik kuralına tabidir. fixtures/payment-response.json dosyasını on iki test paylaşıyorsa, o dosya paylaşılan veritabanı tablosuyla aynı problemi üretir: biri “tutarı 250 yapayım” dediğinde dokuz test düşer ve düşen testlerin hiçbiri ödeme tutarını doğrulamıyordur.

Çözüm aynı: mock yanıtını da factory ile üretin. Temel bir nesne döndüren bir fonksiyon yazın, test kendi ihtiyacı olan alanı override etsin. Mock ve stub arasındaki ayrımı ve nerede hangisinin uygun olduğunu mocking ve stubbing yazısında ele almıştım.

İkinci sorun daha sinsi: mock verisi gerçek şemadan sapabilir. Sağlayıcı alan adını değiştirir, sizin mock’unuz eski adı döndürmeye devam eder, testler yeşil kalır ve üretimde patlar. Bunun tek gerçek çözümü mock verisini sözleşme testine bağlamaktır — kullandığınız sahte yanıtların sağlayıcının yayımladığı şemaya karşı doğrulanması. Mock fixture’larınızı elle yazılmış JSON dosyaları olarak bırakmak yerine, sözleşme doğrulamasından geçen tek bir kaynaktan üretin.

Mevcut paketi kademeli olarak taşımak

Çalışan bir paketi baştan yazmanız gerekmez ve yazmamalısınız; taşıma sırası bellidir.

1. Ölçüm yapın. Paketi --workers=2 ve --shuffle ile çalıştırın, düşen testleri kaydedin. Bu liste, veri sahipliğinin ihlal edildiği yerlerin haritasıdır. Sıralamayı değiştirmeden ölçmek yanıltıcıdır.

2. Dokunulan tabloları çıkarın. Düşen testlerin hangi kayıtları okuduğunu ve hangilerini değiştirdiğini yazın. Genellikle bir avuç tablo (kullanıcı, hesap, ürün) sorunların çoğundan sorumlu çıkar.

3. Önce yazma yapan testleri izole edin. Kirliliği üretenler onlardır. Bu testlere factory fixture’ı verin, seed kaydına dokunmayı bırakın. Bu adımın kendisi, okuma yapan testlerin bir kısmını hiçbir değişiklik yapmadan düzeltir.

4. Sonra okuma yapan testleri taşıyın. Artık kimse seed’i bozmuyor olsa da, “listede en az 3 kayıt var” tarzı assert’ler kırılgandır. Bu testleri kendi ürettikleri kayıtları arayacak şekilde yazın: sayı değil kimlik doğrulayın.

5. Her adımda worker sayısını artırın. 2 worker yeşilse 4’e çıkın. İlerlemeyi “kod daha temiz göründü” ile değil, “8 worker ile paket yeşil” ile ölçün. Bu, taşımanın bittiğini anlamanın tek objektif yoludur.

Bu sırayı bozmayın: temizlik altyapısını kurmadan worker sayısını artırmak, ortamı çöple doldurup ekibin güvenini kaybettirir.

Bundan sonra ne yapmalı

Bu hafta yapabilecekleriniz:

  1. Paketi --workers=2 --shuffle ile çalıştırın ve düşen testlerin listesini bir dosyaya yazın. Düzeltmeyin, sadece listeleyin — bu sizin başlangıç ölçümünüz.
  2. Listenin başındaki testin dokunduğu ana kaydı seçin ve o kayıt için tek bir factory fixture’ı yazın. Yalnızca bir test dosyasını bu fixture’a taşıyın.
  3. Benzersiz kimlik üretimini uniqueTag mantığıyla standartlaştırın ve mevcut Math.random() kullanımlarını arayıp değiştirin.
  4. Temizliği fixture teardown’ına alın ve best-effort hâle getirin; hiçbir temizlik hatası testi kırmasın.
  5. Ortama, qa+ önekli ve 24 saatten eski kayıtları silen zamanlanmış bir iş ekleyin. Bu iş yoksa diğer dört madde zamanla erir.
  6. CI’da worker sayısını bir kademe artırın ve 1. maddedeki listeyle karşılaştırın.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Bu site istenmeyenleri azaltmak için Akismet kullanır. Yorum verilerinizin nasıl işlendiğini öğrenin.