CI (Continuous Integration) kurulumuna ilk başladığımda, olması gerektiğini düşündüğüm bir yazıydı. Resmi belgeler, MySQL hizmet konteynırı eklemeyi gösteriyor. Ancak, her şey doğruyken bile testlerin neden başarısız olabileceğini gösteren 3 sessiz yolu açıklamıyor.
Bu nedenleri açıklayacağım.
SQLite Neden Değil?
SQLite, hızlı olduğu ve kurulum gerektirmediği için Laravel testlerinin varsayılanıdır. Veritabanına dokunmayan birim testler için uygun, ancak özellik testleri için ince bir tuzak oluşturur.
SQLite ve MySQL’in SQL’leri farklıdır. Özellikle:
- Dize birleştirme: SQLite
||kullanırken, MySQLCONCAT()kullanır. - Tarih biçimlendirme: SQLite
strftime(), MySQLDATE_FORMAT()kullanır. - Sütun belirsizlik kuralları SQLite’ta daha hoşgörülüdür.
- Bazı JSON fonksiyonları farklılık gösterir.
Eğer Eloquent sorgularınız ham SQL’e dokunuyorsa (selectRaw, whereRaw, orderByRaw gibi), SQLite bunları geçebilirken MySQL reddedecektir. Bunu üretim aşamasında fark edeceksiniz.
Testlerinizi MySQL üzerinde çalıştırın. Ek kurulum sadece 20 satırlık YAML gerektirir.
Adım 1: MySQL Hizmetini Ekleyin
api-ci.yml dosyanıza, test işinize bir services: bloğu ekleyin:
tests:
name: Tests (PHP 8.4, MySQL 8.4)
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.4
env:
MYSQL_DATABASE: your_app_test
MYSQL_ROOT_PASSWORD: secret
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3options: bloğu, GitHub Actions’ın MySQL’in gerçekten hazır olmasını beklemesini sağlar. Olmadan, göç adımınız MySQL’e bağlanmaya çalışacak ve bağlantı kabul edilmeden başarısız olacaktır. Bu karmaşık bir hata mesajı ile sonuçlanır.
Adım 2: Test Ortamını Geçersiz Kıl
GitHub Actions, yerel .env veya .env.testing dosyalarınızı kullanmaz. Ortam değişkenlerini açıkça ayarlamanız gerekir. En temiz yöntem, .env.example dosyanızı .env.testing içerisine kopyalamak ve ardından geçersiz kılmalar eklemektir:
- name: Copy .env
run: cp .env.example .env.testing
- name: Set test environment variables
run: |
echo "APP_ENV=testing" >> .env.testing
echo "APP_KEY=base64:$(openssl rand -base64 32)" >> .env.testing
echo "DB_CONNECTION=mysql" >> .env.testing
echo "DB_HOST=127.0.0.1" >> .env.testing
echo "DB_PORT=3306" >> .env.testing
echo "DB_DATABASE=your_app_test" >> .env.testing
echo "DB_USERNAME=root" >> .env.testing
echo "DB_PASSWORD=secret" >> .env.testing
echo "QUEUE_CONNECTION=sync" >> .env.testing
echo "BROADCAST_CONNECTION=log" >> .env.testing
echo "CACHE_STORE=array" >> .env.testingNeden BROADCAST_CONNECTION=log? Bu benim başıma geldi. Eğer .env.example dosyanızda BROADCAST_CONNECTION=reverb (Laravel 11’den beri varsayılan) varsa, bir yayın olayı ateşleyen her test — bir ödeme tamamlandığında, bir durum değiştiğinde, bir bildirim gönderildiğinde — 0.0.0.0:8080 adresine TCP bağlantısı açmaya çalışacaktır. CI’da Reverb sunucusu yok. Bu nedenle her istek 500 döner. Testleriniz, belirgin bir açıklama olmadan başarısız olur. Bunun yerine log olarak ayarlamak, yayın olaylarını günlük dosyasına yönlendirir.
Neden QUEUE_CONNECTION=sync? Kuyruğa alınmış işler hemen senkron olarak çalıştırılır ve Redis’e itilmeyerek gerçekleştirilir. Bunu yapmazsanız, bir kontrolcü eylemi iş gönderdiğinde, testler sırasında sessizce çalıştırılmaz.
Adım 3: Göç Edin ve Test Edin
- name: Run migrations
run: php artisan migrate --env=testing --force
env:
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_PORT: "3306"
DB_DATABASE: your_app_test
DB_USERNAME: root
DB_PASSWORD: secret
- name: Run Pest tests
run: ./vendor/bin/pest
env:
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_PORT: "3306"
DB_DATABASE: your_app_test
DB_USERNAME: root
DB_PASSWORD: secretDB ortam değişkenlerini iki kez geçiriyorsunuz — bir kez göç adımına ve bir kez test adımına. GitHub Actions’daki her adım taze bir kabukta çalışır, bu nedenle burada açık olmak sürprizleri önler.
Boş Dizin Tuzağı
Git boş dizinleri takip etmez. Eğer testleriniz storage/framework/views/, storage/framework/sessions/, veya bootstrap/cache/ dizinlerinin mevcut olduğunu öngörüyorsa, temiz bir CI checkout’unda eksik olacaktır.
Laravel’in package:discover (bu, composer install dan sonra çalışır) çerçeveyi başlatır ve bu dizinlere ihtiyaç duyar. Eğer mevcut değillerse, tüm kurulum aşaması “Lütfen geçerli bir önbellek yolu sağlayın” hatasıyla çökebilir.
Düzeltme: Gerekli dizinlerin her birine bir .gitignore yer tutucu ekleyin:
for dir in \
storage/framework/views \
storage/framework/sessions \
storage/framework/cache/data \
storage/logs \
bootstrap/cache; do
mkdir -p api/$dir
printf "*\n!.gitignore" > api/$dir/.gitignore
done
git add api/storage api/bootstrap/cache
git commit -m "chore: add storage skeleton for CI"Boş Test Dizin Tuzağı
Pest, yapılandırılmış bir test dizini yoksa 2 (1 değil) koduyla çıkar. Eğer phpunit.xml dosyanız tests/Unit dizinini listeliyorsa ancak bu dizin boşsa ve asla yüklenmediyse, CI tek bir test çalıştırmadan başarısız olur.
Düzeltme, bir .gitkeep eklemek değildir. Çözüm, phpunit.xml dosyanızda yapılandırdığınız her dizine en az bir anlamlı test eklemektir. CI’nın çalışmadan önce başarısız olması bilgilendirici değildir; CI’nın gerçek bir iddiada başarısız olması, neyin bozulduğunu açıkça belirtir.
Tam Test İş Referansı
tests:
name: Tests (PHP 8.4, MySQL 8.4)
runs-on: ubuntu-latest
needs: quality
services:
mysql:
image: mysql:8.4
env:
MYSQL_DATABASE: your_app_test
MYSQL_ROOT_PASSWORD: secret
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
steps:
- uses: actions/checkout@v4
- name: Setup PHP 8.4
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
extensions: mbstring, pdo, pdo_mysql, bcmath, gd, zip, intl
coverage: none
tools: composer:v2
- name: Cache Composer dependencies
uses: actions/cache@v4
with:
path: api/vendor
key: php-8.4-composer-${{ hashFiles('api/composer.lock') }}
restore-keys: php-8.4-composer-
- name: Install dependencies
run: composer install --no-interaction --prefer-dist --no-progress
- name: Copy .env
run: cp .env.example .env.testing
- name: Set test environment variables
run: |
echo "APP_ENV=testing" >> .env.testing
echo "APP_KEY=base64:$(openssl rand -base64 32)" >> .env.testing
echo "DB_CONNECTION=mysql" >> .env.testing
echo "DB_HOST=127.0.0.1" >> .env.testing
echo "DB_PORT=3306" >> .env.testing
echo "DB_DATABASE=your_app_test" >> .env.testing
echo "DB_USERNAME=root" >> .env.testing
echo "DB_PASSWORD=secret" >> .env.testing
echo "QUEUE_CONNECTION=sync" >> .env.testing
echo "BROADCAST_CONNECTION=log" >> .env.testing
echo "CACHE_STORE=array" >> .env.testing
- name: Run migrations
run: php artisan migrate --env=testing --force
env:
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_PORT: "3306"
DB_DATABASE: your_app_test
DB_USERNAME: root
DB_PASSWORD: secret
- name: Run Pest tests
run: ./vendor/bin/pest
env:
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_PORT: "3306"
DB_DATABASE: your_app_test
DB_USERNAME: root
DB_PASSWORD: secretBir sonraki yazıda, testlerden önce çalışan ve stil kayması veya statik analiz hataları olan herhangi bir PR’yi engelleyen kod kalitesi kapısını ekleyeceğiz.
Bu yazı orijinal olarak dineshstack.com‘da yayımlanmıştır; tam sürümünü kod örnekleri ve güncellemelerle orada okuyabilirsiniz.
Kaynak: Orijinal Makale


