Regression Test Nedir ve Nasıl Otomatize Edilir?
Bir sabah işe geliyorsunuz, ekibiniz dün gece yeni bir özellik deploy etti, her şey yolunda görünüyor. Ama öğleden sonra müşteri desteği aramaya başlıyor: “Ödeme sayfası çalışmıyor.” Bakıyorsunuz, yeni eklenen bir fonksiyon, eskiden mükemmel çalışan bir modülü kırmış. İşte tam bu an, regression testinin neden var olduğunu anlıyorsunuz.
Regression Test Nedir?
Regression test, yazılıma yeni bir değişiklik yapıldıktan sonra mevcut işlevselliğin hâlâ doğru çalıştığını doğrulamak için yapılan test sürecidir. “Regresyon” kelimesi, sistemin daha önce çalışan bir duruma geri dönmesi anlamına gelir. Yani eski ve düzgün çalışan şeylerin yeni değişikliklerden zarar görüp görmediğini kontrol ederiz.
Bu test türü özellikle şu durumlarda kritik öneme sahiptir:
- Yeni özellik ekleme sonrasında
- Bug fix yapıldığında (bir hatayı düzeltirken başka bir hatayı açabilirsiniz)
- Refactoring işlemlerinde
- Kütüphane veya bağımlılık güncellemelerinde
- Veritabanı şema değişikliklerinde
Manuel regression testi küçük projelerde bile birkaç günü alabilir. Orta ölçekli bir uygulamada ise bu süre haftaları bulur. Bu yüzden otomatizasyon burada sadece kolaylık değil, zorunluluktur.
Otomatik Regression Testin Temelleri
Regression testini otomatize etmeden önce neyin test edileceğine karar vermeniz gerekiyor. Her şeyi test etmeye çalışmak hem maliyetli hem de sürdürülemez. Pratikte şu piramit mantığını benimsiyoruz:
- Unit testler: En alt katman, en hızlı çalışan, en çok yazılan
- Integration testler: Bileşenler arası etkileşimi test eder
- End-to-end testler: Kullanıcı akışlarını simüle eder, en yavaş
Regression testleri genellikle bu üç katmana da dokunur ama asıl odak, kritik kullanıcı akışları ve daha önce hata çıkmış noktalardır.
Test Ortamı Kurulumu
Gerçek dünyada test ortamı kurulumu çoğu zaman testlerin kendisinden daha zor olur. Docker ile izole bir ortam kurmak iyi bir başlangıç noktasıdır:
# docker-compose.test.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.test
environment:
- NODE_ENV=test
- DB_HOST=testdb
- REDIS_HOST=testredis
depends_on:
- testdb
- testredis
testdb:
image: postgres:15-alpine
environment:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
tmpfs:
- /var/lib/postgresql/data
testredis:
image: redis:7-alpine
tmpfs:
- /data
tmpfs kullanımına dikkat edin. Test veritabanını disk yerine RAM’de çalıştırmak testleri ciddi ölçüde hızlandırır ve her test başlangıcında temiz bir slate sağlar.
Python ile Regression Test Yazma
Python projelerinde pytest ekosistemi regression testler için oldukça güçlüdür. Basit bir API regression test örneği:
# requirements-test.txt
pytest==7.4.0
pytest-asyncio==0.21.0
httpx==0.25.0
pytest-postgresql==5.0.0
factory-boy==3.3.0
# tests/regression/test_payment_flow.py
import pytest
import httpx
BASE_URL = "http://localhost:8000"
@pytest.fixture(autouse=True)
def reset_db(postgresql):
"""Her testten önce veritabanını temizle"""
yield
postgresql.execute("TRUNCATE TABLE orders, payments CASCADE")
postgresql.commit()
class TestPaymentRegression:
"""Ödeme akışı regression testleri"""
def test_payment_completes_after_cart_update(self, client):
"""
Bug #1247: Sepet güncellemesi sonrası ödeme başarısız oluyordu.
Bu test o bug'ın regrese etmediğini garantiler.
"""
# Sepet oluştur
cart_response = client.post("/api/cart", json={
"user_id": "test-user-001",
"items": [{"product_id": "prod-1", "quantity": 2}]
})
assert cart_response.status_code == 201
cart_id = cart_response.json()["cart_id"]
# Sepeti güncelle (bug'ın tetiklendiği nokta)
update_response = client.put(f"/api/cart/{cart_id}", json={
"items": [{"product_id": "prod-1", "quantity": 3}]
})
assert update_response.status_code == 200
# Ödeme yap - eskiden burada 500 dönüyordu
payment_response = client.post("/api/payments", json={
"cart_id": cart_id,
"payment_method": "credit_card",
"card_token": "tok_test_visa"
})
assert payment_response.status_code == 200
assert payment_response.json()["status"] == "completed"
Testin içine yazdığım yoruma dikkat edin: hangi bug’ı önlediğini ve ticket numarasını belirtin. Altı ay sonra bu testi gören bir ekip arkadaşınız context’i anlayabilsin.
Shell Script ile Smoke Test Otomasyonu
Özellikle legacy sistemlerde veya polyglot ortamlarda, dil-agnostik bir yaklaşım olarak shell script tabanlı regression testler gayet işe yarar:
#!/bin/bash
# regression_smoke_test.sh
set -euo pipefail
BASE_URL="${APP_URL:-http://localhost:8080}"
FAILED_TESTS=0
PASSED_TESTS=0
log_pass() { echo "✓ PASS: $1"; ((PASSED_TESTS++)); }
log_fail() { echo "✗ FAIL: $1"; ((FAILED_TESTS++)); }
assert_http_status() {
local description="$1"
local expected_status="$2"
local url="$3"
local method="${4:-GET}"
local body="${5:-}"
actual_status=$(curl -s -o /dev/null -w "%{http_code}"
-X "$method"
-H "Content-Type: application/json"
${body:+-d "$body"}
"$url")
if [ "$actual_status" -eq "$expected_status" ]; then
log_pass "$description (HTTP $actual_status)"
else
log_fail "$description (Beklenen: $expected_status, Gelen: $actual_status)"
fi
}
# Temel endpoint testleri
assert_http_status "Health check endpoint" 200 "$BASE_URL/health"
assert_http_status "API versiyonu endpoint" 200 "$BASE_URL/api/v1/version"
assert_http_status "Yetkisiz erişim reddi" 401 "$BASE_URL/api/v1/users/profile"
assert_http_status "Olmayan kaynak 404 döner" 404 "$BASE_URL/api/v1/nonexistent"
# POST endpoint testi
assert_http_status "Login endpoint çalışıyor" 200
"$BASE_URL/api/v1/auth/login"
"POST"
'{"username":"testuser","password":"testpass123"}'
echo ""
echo "Sonuç: $PASSED_TESTS geçti, $FAILED_TESTS başarısız"
[ "$FAILED_TESTS" -eq 0 ] || exit 1
Bu scripti production deploy öncesi ve sonrası çalıştırmak, temel işlevselliğin ayakta olduğunu hızla doğrular.
CI/CD Pipeline’a Entegrasyon
Regression testlerin gerçek değeri, her kod değişikliğinde otomatik olarak çalıştırıldığında ortaya çıkar. GitLab CI örneği:
# .gitlab-ci.yml
stages:
- build
- unit-test
- integration-test
- regression-test
- deploy
variables:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
regression-tests:
stage: regression-test
image: python:3.11-slim
services:
- name: postgres:15-alpine
alias: testdb
- name: redis:7-alpine
alias: testredis
before_script:
- pip install -r requirements-test.txt
- python manage.py migrate --settings=config.test_settings
- python manage.py loaddata tests/fixtures/regression_baseline.json
script:
- pytest tests/regression/
--tb=short
--junitxml=reports/regression-results.xml
-v
--timeout=120
artifacts:
when: always
reports:
junit: reports/regression-results.xml
expire_in: 30 days
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main"'
allow_failure: false
allow_failure: false satırı kritik. Regression test başarısız olursa pipeline durmalı ve merge engellenmelidir. Bazı ekipler bunu esnekleştirmek ister, ama bu kötü bir alışkanlık.
Test Verisi Yönetimi
Regression testlerin en sık tökezlediği yer test verisi yönetimidir. Sabit (hardcoded) veri kullanan testler zamanla kırılganlaşır. Factory pattern kullanın:
# tests/factories.py
import factory
from factory.django import DjangoModelFactory
from decimal import Decimal
import random
class UserFactory(DjangoModelFactory):
class Meta:
model = 'accounts.User'
username = factory.Sequence(lambda n: f'testuser_{n}')
email = factory.LazyAttribute(lambda obj: f'{obj.username}@example.com')
is_active = True
class ProductFactory(DjangoModelFactory):
class Meta:
model = 'catalog.Product'
name = factory.Faker('product_name', locale='tr_TR')
price = factory.LazyFunction(
lambda: Decimal(str(round(random.uniform(10.0, 1000.0), 2)))
)
stock = factory.LazyFunction(lambda: random.randint(1, 100))
is_available = True
class OrderFactory(DjangoModelFactory):
class Meta:
model = 'orders.Order'
user = factory.SubFactory(UserFactory)
status = 'pending'
total_amount = factory.LazyFunction(
lambda: Decimal(str(round(random.uniform(50.0, 5000.0), 2)))
)
# Kullanım
# tests/regression/test_order_regression.py
def test_order_status_transitions():
order = OrderFactory(status='pending')
# test devam eder...
Paralel Test Çalıştırma ve Hız Optimizasyonu
Regression test suitleri büyüdükçe çalışma süresi uzar. 300 testin 20 dakika sürmesi, geliştiricilerin testi atlamasına yol açar. pytest-xdist ile paralel çalıştırın:
# Mevcut CPU sayısına göre otomatik worker belirleme
pytest tests/regression/ -n auto --dist=worksteal
# Belirli worker sayısı ile
pytest tests/regression/ -n 4
# Yavaş testleri profillemek için
pytest tests/regression/ --durations=10
Paralel test çalıştırmada dikkat edilmesi gereken en önemli nokta test izolasyonudur. Testler birbirinin verisine dokunmamalıdır. Bu yüzden her test için unique ID’ler ve transaction rollback kullanın:
# conftest.py
import pytest
from django.test import TestCase
@pytest.fixture(scope='function')
def db_transaction(db):
"""
Her test fonksiyonu için transaction başlat ve rollback yap.
Paralel testlerde data isolation sağlar.
"""
from django.db import transaction
with transaction.atomic():
savepoint = transaction.savepoint()
yield
transaction.savepoint_rollback(savepoint)
Gerçek Dünya Senaryosu: Legacy Sistem Regression Suite
Hiç sıfırdan regression suite kurmak zorunda kaldınız mı? Gerçekten zorlu bir süreç. Bir e-ticaret sisteminde bunu nasıl yaptığımızı paylaşayım.
Önce mevcut sistemi kara kutu olarak ele alıp kritik akışları belgeledik. Sonra bu akışları API seviyesinde test ettik:
#!/bin/bash
# scripts/capture_baseline.sh
# Mevcut sistemin davranışını baseline olarak kaydet
BASELINE_DIR="tests/baselines"
API_URL="http://production-readonly.internal"
mkdir -p "$BASELINE_DIR"
endpoints=(
"/api/v1/products?category=electronics&limit=10"
"/api/v1/products/featured"
"/api/v1/categories"
"/api/v1/config/checkout"
)
for endpoint in "${endpoints[@]}"; do
filename=$(echo "$endpoint" | sed 's/[^a-zA-Z0-9]/_/g' | sed 's/__*/_/g')
echo "Capturing: $endpoint"
curl -s "$API_URL$endpoint" | python3 -m json.tool > "$BASELINE_DIR/${filename}.json"
echo "Saved to: $BASELINE_DIR/${filename}.json"
done
echo "Baseline capture tamamlandı: $BASELINE_DIR"
Sonra bu baseline’ları regression testlerde referans olarak kullandık. Yeni deploy sonrası aynı endpointlerin response’larını baseline ile karşılaştırdık. Response schema değişirse alarm çalıştı.
Flaky Test Sorunu
Regression testlerin en sinir bozucu düşmanı “flaky test”lerdir. Bazen geçer, bazen geçmez. Sebebini bulmak saatler alabilir. Genellikle şu sebeplerden kaynaklanır:
- Race condition (asenkron işlemlerde bekleme eksikliği)
- Hardcoded zaman değerleri (
time.sleep(2)gibi) - Test sırası bağımlılığı
- Dış servis bağımlılığı
Flaky testleri tespit etmek için:
# Aynı testi 10 kez çalıştır, kaçı geçiyor?
pytest tests/regression/test_payment_flow.py
--count=10
-v
2>&1 | grep -E "(PASSED|FAILED|ERROR)"
# pytest-rerunfailures ile flaky testleri geçici olarak yönet
# ama asıl sorunu mutlaka düzelt
pytest tests/regression/ --reruns=2 --reruns-delay=1
Flaky test bulduğunuzda hemen xfail veya skip ile işaretleyin ama bir ticket açın ve takip edin. “Zaten bazen geçiyor” diyerek bırakmayın.
Test Sonuçlarını İzleme ve Raporlama
Regression testlerin ne zaman, neden başarısız olduğunu takip etmek önemlidir. Sadece “geçti/geçmedi” değil, trend analizi yapın:
# pytest-html ile görsel rapor
pytest tests/regression/
--html=reports/regression_$(date +%Y%m%d_%H%M%S).html
--self-contained-html
# Allure ile daha detaylı raporlama
pytest tests/regression/ --alluredir=./allure-results
allure generate ./allure-results -o ./allure-report --clean
allure open ./allure-report
Allure raporları özellikle ekibe regression test durumunu sunmak için çok değerlidir. Hangi test suitelarının ne kadar sürdüğünü, geçmiş run’larla karşılaştırmalı başarı oranlarını görsel olarak sunabilirsiniz.
Regression Test Yazarken Yapılan Yaygın Hatalar
Uzun yıllar boyunca pek çok ekipte gördüğüm hatalar:
- Çok geniş scope: “Her şeyi test edelim” yaklaşımı. Önce kritik path’leri belirleyin.
- Test bağımlılığı: A testi B testinin çıktısına bakıyor. Her test bağımsız çalışabilmeli.
- Yavaş testleri görmezden gelme: 5 dakika süren bir test kimse tarafından sık çalıştırılmaz.
- Başarısız testleri skip etme: “Şimdi vakti yok” derken teknik borç birikir.
- Sadece happy path testi: Edge case’leri, hata durumlarını ve sınır değerlerini test edin.
- Test ortamını production’dan farklı tutma: Test geçiyor ama production’da patlıyor. Ortam farkı genellikle sebeptir.
Sonuç
Regression testi, “bu yine bozuldu mu” sorusunu sistematik ve güvenilir bir şekilde cevaplamanın tek yoludur. Başlangıçta test yazmak zaman alır, doğru. Ama bir kez kurulduğunda, her deployment’ta saatler kazandırır ve geceleri rahat uyumanızı sağlar.
Sıfırdan başlıyorsanız önerin şu olsun: dün müşteriden gelen ilk complaint’i, dün production’da kapattığınız ilk bug’ı alın ve onun için bir regression testi yazın. Sonra bir daha, bir daha. Bir ay sonra bakarsınız, farkında olmadan solid bir suite oluşmuş.
Otomatizasyon tarafında da mükemmeli beklemeyin. CI’a bağlamak için beş dakika yetersizse, en azından her sabah manuel tetiklenebilir hale getirin. Sonra CI’a taşırsınız. Süreç iteratif.
En iyi regression test, yazılmış ve çalışan olandır.
