Veritabanı Yönetimi

    Test Verisi Üretme ve Seed Stratejileri

    Testlerin güvenilir çalışması için doğru test verisini üretmenin ve yönetmenin yolları.

    12 dk okuma Güncellendi: 25 Ağustos 2026

    Testlerinizin ne kadar güvenilir olduğu, büyük ölçüde hangi veriyle çalıştıklarına bağlıdır. Boş bir veritabanında yeşil yanan test paketi, üretimdeki bir milyon satırlı tabloda çöken bir sorguyu asla yakalayamaz. Öte yandan üretim veritabanının kopyasını test ortamına almak da çözüm değildir; hem KVKK açısından ciddi bir risk yaratır hem de testleri kimsenin anlamadığı, tekrar üretilemeyen bir veri yığınına bağımlı kılar. Test verisi üretme ve seed stratejileri, bu iki uç arasında sağlam bir orta yol kurmakla ilgilidir.

    Bu rehberde önce üretim verisini kopyalamanın neden kötü bir alışkanlık olduğunu netleştireceğiz, sonra üç temel stratejiyi (sabit fixture, sentetik üretim, maskelenmiş kopya) hangi durumda hangisinin doğru olduğuyla birlikte ele alacağız. SQL'in kendi araçlarıyla milyonlarca satırı saniyeler içinde nasıl üreteceğinizi, seed dosyalarını migration'dan neden ayırmanız gerektiğini, her testin temiz bir durumdan başlaması için kullanılan izolasyon tekniklerini ve performans testleri için gerçekçi hacim üretmeyi göstereceğim.

    Üretim Verisini Kopyalamak Neden Kötü Bir Fikir#

    "Test ortamına canlının bir kopyasını alalım" cümlesi masumdur ama ardında dört ayrı problem taşır.

    Birincisi hukukidir. Üretim veritabanınızda ad, soyad, e-posta, telefon ve adres gibi kişisel veriler bulunuyorsa, bunları test ortamına kopyalamak KVKK açısından bir veri işleme faaliyetidir ve test ortamları neredeyse her zaman üretimden daha zayıf korunur. Geliştirici dizüstü bilgisayarına inen bir test veritabanı dökümü, en sık gözden kaçan sızıntı kanallarından biridir. Bu konunun tamamı için veritabanı anonimleştirme ve KVKK uyumu yazısına bakın.

    İkincisi tekrarlanabilirlik problemidir. Üretim verisi sürekli değişir; bugünkü kopyayla geçen testler, yarınki kopyayla kırılabilir. Test paketinin sonucu, kodun doğruluğundan bağımsız olarak dalgalanmaya başlar ve ekip bir süre sonra kırmızı testlere güvenmemeyi öğrenir — ki bu, test yazmamaktan daha kötüdür.

    Üçüncüsü kapsama problemidir. Üretim verisi "normal" durumları içerir; testlerin asıl yakalaması gereken uç durumları (boş alan, çok uzun metin, sıfır tutar, negatif bakiye, aynı anda iki kayıt) içermez. Bunları elle eklemeniz gerekir.

    Dördüncüsü boyut problemidir. 200 GB'lık bir üretim kopyasını her testte geri yüklemek, test süresini kabul edilemez düzeye çıkarır.

    KriterÜretim kopyasıSentetik veriSabit fixture
    KVKK riskiYüksekYokYok
    TekrarlanabilirlikDüşükYüksekÇok yüksek
    Uç durum kapsamıRastgeleKontrollüTam kontrollü
    Kurulum hızıYavaşHızlıÇok hızlı
    Gerçekçi hacimVarÜretilebilirYok

    Üç Strateji ve Hangisi Ne Zaman#

    Sabit fixture, testin ihtiyaç duyduğu az sayıda kaydı elle tanımlamanızdır. Bir birim testinin ya da tek bir iş kuralını doğrulayan bir entegrasyon testinin ihtiyacı budur: üç müşteri, iki ürün, bir sipariş. Avantajı, testi okuyan kişinin veriyi de görebilmesidir; dezavantajı, sayı arttıkça bakımının zorlaşmasıdır.

    Sentetik üretim, kurallara göre programatik olarak veri üretmektir. Yüz bin müşteri, her birine rastgele sayıda sipariş, siparişlere rastgele kalemler. Performans testleri, sayfalama testleri ve raporlama doğrulamaları için doğru seçim budur. Kritik nokta, üreticiyi deterministik kılmaktır: aynı tohum (seed) değeriyle her seferinde aynı veri üretilmelidir, aksi hâlde kırılan bir testi yeniden üretemezsiniz.

    Maskelenmiş kopya, üretim verisinin kişisel alanları anonimleştirilmiş hâlidir. Yalnızca gerçek veri dağılımının kritik olduğu durumlarda (sorgu planı analizi, kapasite planlama) ve yalnızca sıkı erişim kontrolü altındaki bir ön üretim ortamında kullanılmalıdır; geliştirici makinelerine hiçbir zaman inmemelidir.

    Pratikte olgun bir projede üçü de bulunur: birim testleri fixture ile, entegrasyon testleri küçük sentetik setlerle, performans testleri büyük sentetik setlerle çalışır ve maskelenmiş kopya yalnızca ön üretimde durur.

    SQL ile Toplu Veri Üretimi#

    Harici bir araca gerek kalmadan, veritabanının kendi araçlarıyla milyonlarca satır üretebilirsiniz ve bu yöntem şaşırtıcı derecede hızlıdır. PostgreSQL'de generate_series bunun için biçilmiş kaftandır:

    -- Deterministik olsun diye rastgele üreteci sabit bir tohuma bağlıyoruz
    SELECT setseed(0.42);
    
    -- 100.000 müşteri üret
    INSERT INTO musteriler (ad, soyad, eposta, sehir, olusturuldu)
    SELECT
        'Ad'    || i,
        'Soyad' || i,
        'kullanici' || i || '@ornek.com',
        (ARRAY['İstanbul','Ankara','İzmir','Bursa','Antalya'])[1 + (i % 5)],
        now() - (random() * interval '730 days')
    FROM generate_series(1, 100000) AS s(i);
    
    -- Her müşteriye 0-9 arası sipariş üret (referans bütünlüğü korunur)
    INSERT INTO siparisler (musteri_id, tutar, durum, olusturuldu)
    SELECT
        m.id,
        round((random() * 4900 + 100)::numeric, 2),
        (ARRAY['bekliyor','odendi','kargoda','tamamlandi','iptal'])[1 + floor(random()*5)::int],
        m.olusturuldu + (random() * interval '365 days')
    FROM musteriler m
    CROSS JOIN generate_series(1, 10) g
    WHERE random() < 0.6;   -- her müşteri için her satır %60 olasılıkla üretilir
    

    MySQL'de generate_series yoktur ama özyinelemeli CTE aynı işi görür:

    -- MySQL 8: özyineleme derinliği varsayılan 1000, artırmanız gerekir
    SET SESSION cte_max_recursion_depth = 200000;
    
    INSERT INTO musteriler (ad, soyad, eposta, sehir)
    WITH RECURSIVE sayac (i) AS (
        SELECT 1
        UNION ALL
        SELECT i + 1 FROM sayac WHERE i < 100000
    )
    SELECT
        CONCAT('Ad', i),
        CONCAT('Soyad', i),
        CONCAT('kullanici', i, '@ornek.com'),
        ELT(1 + (i % 5), 'İstanbul','Ankara','İzmir','Bursa','Antalya')
    FROM sayac;
    

    Toplu yükleme yaparken üç ayarı geçici olarak değiştirmek süreyi belirgin biçimde kısaltır: indeksleri veri yüklendikten sonra oluşturun, yabancı anahtar kontrollerini yükleme sırasında kapatın ve yüklemeyi tek bir transaction içinde yapın. Yükleme bittiğinde hepsini geri açmayı unutmayın.

    -- PostgreSQL: yükleme sonrası indeks ve istatistik
    CREATE INDEX idx_siparis_musteri ON siparisler (musteri_id, olusturuldu DESC);
    ANALYZE siparisler;   -- planlayıcının doğru karar vermesi için şart
    

    ANALYZE adımı atlanırsa sorgu planlayıcı tabloyu hâlâ boş sanır ve performans testiniz gerçekle ilgisi olmayan planlar üretir. psql tarafındaki temel komutlara alışkın değilseniz PostgreSQL temel komutlar yazısı hızlı bir referans sunuyor.

    Gerçekçi Veri: Faker ve Türkçe İçerik#

    Sayı ekleyerek üretilen Ad1, Ad2 biçimindeki veriler yapısal testler için yeterlidir ama arayüz testlerinde ve müşteriye gösterilen demolarda yetersiz kalır. Bu noktada Faker türü kütüphaneler devreye girer; Türkçe yerel ayarıyla gerçekçi ad, adres ve şirket unvanı üretebilirler.

    # Python: deterministik tohum ile Türkçe test verisi üretimi
    from faker import Faker
    import csv
    
    fake = Faker("tr_TR")
    Faker.seed(42)          # aynı tohum → her çalıştırmada aynı veri
    
    with open("/tmp/musteriler.csv", "w", newline="", encoding="utf-8") as f:
        w = csv.writer(f)
        w.writerow(["ad", "soyad", "eposta", "sehir", "telefon"])
        for i in range(100_000):
            w.writerow([
                fake.first_name(),
                fake.last_name(),
                f"kullanici{i}@ornek.com",   # gerçek domain kullanmayın
                fake.city(),
                fake.phone_number(),
            ])
    

    Ürettiğiniz CSV'yi veritabanına yüklemek, satır satır INSERT çalıştırmaktan onlarca kat hızlıdır:

    # PostgreSQL: sunucu tarafı COPY (dosya sunucuda olmalı) ya da istemci tarafı \copy
    psql -d uygulama -c "\copy musteriler(ad,soyad,eposta,sehir,telefon) FROM '/tmp/musteriler.csv' CSV HEADER"
    
    # MySQL: LOCAL anahtar kelimesi dosyanın istemcide olduğunu söyler
    mysql --local-infile=1 uygulama -e "
    LOAD DATA LOCAL INFILE '/tmp/musteriler.csv'
    INTO TABLE musteriler
    FIELDS TERMINATED BY ',' ENCLOSED BY '\"'
    IGNORE 1 LINES
    (ad, soyad, eposta, sehir, telefon);"
    

    E-posta adreslerinde gerçek alan adı kullanmayın. Test ortamınız yanlışlıkla e-posta göndermeye kalkarsa, o adreslerin gerçek sahiplerine mesaj gider. ornek.com gibi belgelenmiş örnek alan adları ya da kendi kontrolünüzdeki bir alt alan adı kullanın. Aynı kural telefon numaraları için de geçerlidir; SMS entegrasyonu olan bir sistemde gerçek numaralarla test yapmak, tanımadığınız insanlara mesaj göndermek demektir.

    Seed'i Migration'dan Ayırmak#

    Sık yapılan bir tasarım hatası, test verisini migration dosyalarının içine gömmektir. Migration'lar tüm ortamlarda çalışır ve üretime de gider; test verisini oraya koyarsanız canlı veritabanınızda Ad1 Soyad1 isimli müşteriler belirir. İki veri tipini net biçimde ayırın:

    Referans verisi (ülke listesi, para birimleri, KDV oranları, rol tanımları) uygulamanın çalışması için gereklidir ve her ortamda bulunmalıdır. Bunu migration'a koyabilirsiniz.

    Test verisi (örnek müşteriler, sahte siparişler) yalnızca geliştirme ve test ortamında bulunmalıdır. Bunu ayrı bir seed mekanizmasına alın.

    Liquibase kullanıyorsanız bu ayrım context özelliğiyle doğal biçimde yapılır:

      - changeSet:
          id: 2026-08-25-referans-para-birimleri
          author: ekip
          changes:
            - insert:
                tableName: para_birimleri
                columns:
                  - column: {name: kod, value: TRY}
                  - column: {name: ad, value: Türk Lirası}
    
      - changeSet:
          id: 2026-08-25-ornek-musteriler
          author: ekip
          context: test,dev          # üretimde ASLA çalışmaz
          changes:
            - sqlFile:
                path: seed/ornek-musteriler.sql
    

    Flyway tarafında ise seed dosyalarını ayrı bir dizine koyup yalnızca ilgili ortamda flyway.locations değerine eklersiniz. Migration disiplininin tamamı için veritabanı migration yönetimi yazısına bakabilirsiniz.

    Seed dosyalarını idempotent yazmak da işinizi kolaylaştırır: aynı seed iki kez çalıştığında hata vermemeli, mükerrer kayıt oluşturmamalıdır.

    -- İdempotent seed: ikinci çalıştırmada hiçbir şey değişmez
    INSERT INTO roller (kod, ad) VALUES
        ('admin',  'Yönetici'),
        ('editor', 'Editör'),
        ('viewer', 'İzleyici')
    ON CONFLICT (kod) DO NOTHING;
    

    Test İzolasyonu: Her Test Temiz Başlasın#

    Testlerin birbirini etkilemesi, hata ayıklaması en zor test problemidir: tek başına çalıştırınca geçen bir test, paket hâlinde çalıştırınca kırılır. Çözüm, her testin bilinen bir durumdan başlamasını garanti etmektir. Üç yaygın yöntem vardır:

    1. Transaction içinde çalıştır, sonunda geri al. Her test bir transaction açar, işini yapar, ROLLBACK ile çıkar. En hızlı yöntemdir ama testiniz kendi içinde transaction kullanıyorsa ya da birden fazla bağlantı açıyorsa uygulanamaz.
    2. Her testten önce tabloları temizle. TRUNCATE ile ilgili tabloları boşaltıp seed'i yeniden yükleyin.
    3. Şablon veritabanından kopyala. Seed'i bir kez yükleyip şablon veritabanı oluşturur, her test paketi için ondan hızlı bir kopya çıkarırsınız.
    -- 2. yöntem: kimlik sayaçlarını da sıfırlayarak temizlik
    TRUNCATE TABLE siparis_kalemleri, siparisler, musteriler
        RESTART IDENTITY CASCADE;
    
    -- 3. yöntem: PostgreSQL'de şablon veritabanından hızlı kopya
    CREATE DATABASE test_calisma TEMPLATE test_sablon;
    

    RESTART IDENTITY ifadesi önemlidir; onu yazmazsanız otomatik artan kimlikler kaldığı yerden devam eder ve "id 1 olan müşteri" varsayımı üzerine kurulu testler kırılır. CASCADE ise yabancı anahtarla bağlı tabloların da temizlenmesini sağlar.

    Konteyner tabanlı bir yaklaşım da giderek yaygınlaşıyor: her test koşusu için tek kullanımlık bir veritabanı konteyneri başlatmak. Bu yöntem tam izolasyon sağlar ama konteyner başlatma süresi test döngüsüne eklenir; küçük test paketlerinde transaction geri alma yöntemi çok daha pratiktir.

    Performans Testleri için Gerçekçi Hacim#

    Sorgu performansını test etmek istiyorsanız iki şeyi doğru yapmanız gerekir: yeterli satır sayısı ve gerçekçi dağılım. İkincisi çoğu zaman atlanır. Her müşteriye tam olarak 10 sipariş verirseniz, sorgu planlayıcı bunu tekdüze bir dağılım olarak görür; gerçek hayatta ise müşterilerin çoğu 1-2 sipariş verir, küçük bir azınlık yüzlerce. Bu çarpık dağılım, indeks seçimi ve birleştirme stratejisini doğrudan etkiler.

    -- Çarpık (Pareto benzeri) dağılım: az sayıda müşteri çok sipariş verir
    INSERT INTO siparisler (musteri_id, tutar, olusturuldu)
    SELECT
        m.id,
        round((random() * 4900 + 100)::numeric, 2),
        now() - (random() * interval '365 days')
    FROM musteriler m
    CROSS JOIN LATERAL generate_series(
        1,
        CASE WHEN random() < 0.80 THEN 2      -- %80 müşteri: 2 sipariş
             WHEN random() < 0.97 THEN 15     -- %17 müşteri: 15 sipariş
             ELSE 300 END                     -- %3 müşteri: 300 sipariş
    ) g;
    

    Veriyi yükledikten sonra mutlaka istatistikleri güncelleyin ve gerçek bir sorgunun planına bakın:

    -- Gerçek yürütme planı ve süre; sadece EXPLAIN yeterli değildir
    EXPLAIN (ANALYZE, BUFFERS)
    SELECT m.ad, count(*) AS siparis_sayisi, sum(s.tutar) AS toplam
    FROM musteriler m
    JOIN siparisler s ON s.musteri_id = m.id
    WHERE s.olusturuldu > now() - interval '30 days'
    GROUP BY m.id, m.ad
    ORDER BY toplam DESC
    LIMIT 20;
    

    MySQL tarafında yavaş sorguları yakalamanın yöntemi için MySQL yavaş sorgu bulma yazısı doğrudan bu adımı ele alıyor. Performans testlerini üretim sunucunuzda değil, ayrı bir ortamda çalıştırın; test yükü canlı trafiği etkilerse ölçtüğünüz sayılar da anlamsızlaşır. Bunun için ayrı bir VDS ya da bulut sunucu örneği en temiz çözümdür.

    Sık Yapılan Hatalar#

    Rastgeleliği tohumlamamak en yaygın hatadır. Tohumsuz rastgele üretimle her çalıştırmada farklı veri oluşur; bir test kırıldığında aynı durumu yeniden üretemezsiniz. Hem SQL tarafında (setseed) hem üretim betiğinde (Faker.seed) tohumu sabitleyin ve tohum değerini test çıktısına yazdırın.

    Referans bütünlüğünü bozan seed yazmak ikinci hatadır. Yabancı anahtar kısıtı olan bir tabloya, karşılığı olmayan bir kimlikle satır eklemeye çalışmak seed'i yarıda kırar. Seed'i her zaman bağımlılık sırasına göre yazın: önce müşteriler, sonra siparişler, sonra sipariş kalemleri. Temizlik ise ters sırada yapılır.

    Test verisini üretimde çalıştırmak üçüncüsüdür ve en pahalısıdır. Seed betiğinin hangi ortamda çalıştığını kontrol eden bir güvenlik kilidi ekleyin; ortam değişkeni production ise betik hiçbir şey yapmadan çıkmalıdır. Bu üç satırlık kontrol, bir gün gerçekten işe yarar.

    Gerçek e-posta ve telefon kullanmak dördüncüsüdür. Test ortamınızın e-posta gönderimini de sahte bir SMTP sunucusuna yönlendirin; kendi altyapınızda deneme yaparken SMTP test aracı ile bağlantı ve kimlik doğrulama ayarlarını gerçek gönderim yapmadan doğrulayabilirsiniz.

    Sıkça Sorulan Sorular#

    Test veritabanına üretim verisini kopyalayabilir miyim#

    Kişisel veri içeriyorsa doğrudan kopyalamamalısınız. KVKK açısından bu bir veri işleme faaliyetidir ve test ortamları genellikle üretimden daha zayıf korunduğu için risk yüksektir. Gerçek veri dağılımına ihtiyacınız varsa doğru yöntem, kopyayı alırken kişisel alanları anonimleştirmek ve sonucu yalnızca erişimi kısıtlı bir ön üretim ortamında tutmaktır. Geliştirici makinelerine inen kopyalar için sentetik veri tek makul seçenektir.

    Ne kadar test verisi üretmeliyim#

    Bu, testin amacına göre değişir. Birim ve entegrasyon testleri için birkaç düzine kayıt yeterlidir ve hızlı çalışır. Sayfalama, sıralama ve filtreleme mantığını doğrulamak için birkaç bin satır gerekir. Sorgu performansını ve indeks davranışını test etmek içinse üretimdeki büyüklük sırasına yakın bir hacim üretmeniz gerekir; 500 satırda hızlı görünen bir sorgu, 5 milyon satırda tamamen farklı bir plan seçebilir.

    Seed verisi migration dosyasına konur mu#

    Referans verisi konur, test verisi konmaz. Ülke listesi, para birimleri, rol tanımları ve KDV oranları gibi uygulamanın çalışması için gerekli olan kayıtlar tüm ortamlarda bulunmalıdır ve migration bunun doğru yeridir. Örnek müşteriler ve sahte siparişler ise yalnızca geliştirme ve test ortamına aittir; bunları ayrı bir seed mekanizmasına alın ve ortam kontrolüyle koruyun. Liquibase'in context özelliği bu ayrımı doğal biçimde sağlar.

    Testler birbirini etkiliyor, nasıl izole ederim#

    En hızlı yöntem her testi bir transaction içinde çalıştırıp sonunda geri almaktır; veritabanına hiçbir kalıcı değişiklik yazılmaz. Bu yöntem uygulanamıyorsa, her testten önce ilgili tabloları TRUNCATE ... RESTART IDENTITY CASCADE ile temizleyip seed'i yeniden yükleyin. Kimlik sayaçlarını sıfırlamayı atlamayın; sabit kimlik varsayan testler aksi hâlde rastgele kırılır. Tam izolasyon gerekiyorsa test koşusu başına tek kullanımlık bir veritabanı ya da konteyner kullanabilirsiniz.

    Faker ile üretilen veriler gerçekten güvenli mi#

    Faker rastgele ad, adres ve numara üretir; bu değerlerin gerçek kişilerle eşleşmesi tesadüf dışında mümkün değildir ve kişisel veri sayılmazlar. Ancak iki noktaya dikkat edin: e-posta ve telefon alanlarında gerçek alan adı veya gerçek numara biçimi kullanırsanız, test ortamınız yanlışlıkla mesaj gönderdiğinde tanımadığınız kişilere ulaşabilir. Ayrıca ürettiğiniz veriyi bir tohumla sabitlemezseniz, testlerin tekrarlanabilirliğini kaybedersiniz.

    Büyük seed dosyaları testleri yavaşlatıyor, ne yapabilirim#

    Önce seed'i test tipine göre bölün: birim testleri büyük veri setine ihtiyaç duymaz. İkinci adım, veriyi her testte değil test paketi başında bir kez yüklemek ve testleri transaction geri almasıyla izole etmektir. Üçüncü adım, PostgreSQL'de şablon veritabanı kullanmaktır; seed'i bir kez yükleyip her koşuda ondan kopya çıkarmak, veriyi yeniden yüklemekten kat kat hızlıdır. Son olarak toplu yüklemede satır satır INSERT yerine COPY veya LOAD DATA kullanın.

    Kapanış#

    İyi test verisi, testlerin kendisinden daha az konuşulan ama sonuçlarını doğrudan belirleyen bir konu. Aklınızda kalması gereken dört alışkanlık şunlar: üretim verisini olduğu gibi kopyalamayın, ihtiyacınız gerçek dağılımsa anonimleştirin; rastgele üretimi mutlaka bir tohumla sabitleyin ki kırılan testi yeniden üretebilesiniz; referans verisi ile test verisini net biçimde ayırın ve test verisinin üretimde çalışmasını ortam kontrolüyle engelleyin; her testin bilinen bir durumdan başlamasını transaction geri alma ya da temizlik adımıyla garanti edin.

    Test ve staging veritabanlarını üretimden ayrı tutmak, hem güvenlik hem performans ölçümü açısından doğru olanıdır. İzole bir test ortamı için VDS veya bulut sunucu paketleri uygun bir başlangıç sunar; uygulamanız paylaşımlı bir ortamda çalışıyorsa web hosting paketlerimizde ayrı bir test veritabanı oluşturabilirsiniz. Kurulum, izolasyon ve düzenli yedek disiplinini devretmek isterseniz sunucu yönetimi hizmetimiz bu yükü üstlenebilir.

    TestSeedVeritabanı

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.