Bios.Testing 0.2.0
Bios.Testing
Gerçek altyapıyla test için fikstürler. Bugün tek parça var:
PostgresContainerFixture.
Satır seviyesi güvenlik ve rol ayrımı güvenlik modelinin kendisiyse, sahte bir veritabanı ya da süper kullanıcı bağlantısı hiçbir şey kanıtlamaz: süper kullanıcı bu korumaların hepsini her koşulda aşar. Bu fikstür Testcontainers ile gerçek bir PostgreSQL kaldırır, ürünün kendi göçlerini sırayla uygular ve ürünün gerçek uygulama rollerine parola verir; testler o rollerle bağlanır.
Göçler bir kopyadan değil çalışma ağacından okunur; yeni bir göç eklendiğinde fikstürün haberi olur, kimsenin bir listeyi elle güncellemesi gerekmez.
Bağlama
Ürün kendi fikstürünü türetir ve kendi değerlerini doldurur. Taban sınıfın
yapıcısı protected, PostgresFixtureOptions alanlarının çoğu required;
yani aşağıdaki alanların hepsi zorunludur, biri eksikse kod derlenmez.
using Bios.Testing;
public sealed class UrunPostgresFixture : PostgresContainerFixture
{
public UrunPostgresFixture() : base(new PostgresFixtureOptions
{
Image = "postgres:16-alpine",
Database = "urun",
Owner = "urun_sahibi", // konteynerin süper kullanıcısı
OwnerPassword = "test-owner",
Roles = ["urun_api", "urun_worker"], // göçlerin parolasız yarattığı roller
RolePassword = "test-role",
MigrationsDirectory = Path.Combine(
AppContext.BaseDirectory, "..", "..", "..", "..", "..", "db", "migrations"),
ResetSql = "truncate table belge, fis restart identity cascade",
})
{ }
}
public sealed class BelgeTests : IClassFixture<UrunPostgresFixture>
{
private readonly UrunPostgresFixture _fixture;
public BelgeTests(UrunPostgresFixture fixture) => _fixture = fixture;
[Fact]
public async Task Api_rolu_baskasinin_satirini_goremez()
{
await _fixture.ResetAsync();
await using var connection = new NpgsqlConnection(
_fixture.ConnectionStringFor("urun_api"));
await connection.OpenAsync();
// ... gerçek rolle, gerçek şemaya karşı
}
}
Sunulan üç üye:
| Üye | Ne verir |
|---|---|
Task<NpgsqlConnection> OpenAsOwnerAsync() |
Süper kullanıcı bağlantısı; hazırlık ve temizlik için |
string ConnectionStringFor(string role) |
O role ait bağlantı metni, havuzlama kapalı |
Task ResetAsync() |
ResetSql cümlesini çalıştırır |
Yapılandırma
Bu paketin appsettings bölümü yoktur; her şey yukarıdaki
PostgresFixtureOptions kaydından gelir. İsteğe bağlı tek alan Environment:
konteynere fazladan ortam değişkeni geçirir (bir yerel ayar, bir initdb
anahtarı).
Environment = new Dictionary<string, string>
{
["POSTGRES_INITDB_ARGS"] = "--locale=tr_TR.UTF-8",
},
Dikkat
Göç sırası bayt sırasıdır, sayı sırası değil. MigrationsDirectory
içindeki *.sql dosyaları tam yola göre StringComparer.Ordinal ile
sıralanır. Bunun anlamı şudur: 10.sql dosyası 2.sql dosyasından önce
uygulanır, çünkü '1' karakteri '2' karakterinden önce gelir.
Bu bilerek böyledir ve "akıllı" bir sıralamayla düzeltilmemelidir. Gerçek
dağıtım betiği aynı klasörü bir kabuk kalıbıyla (migrations/*.sql) uygular
ve kabuk da tam olarak böyle sıralar. Fikstür daha akıllı sıralasaydı,
testler dağıtımın hiç kullanmadığı bir sırayı doğrulardı; yani bu testlerin
yakalaması gereken sıra hatasını gizlerdi.
Çare sıralamada değil, isimlerde: dosya adlarını sabit genişlikte sıfırla
doldurun (001_init.sql, 002_kiracilik.sql, ... 054_x.sql). O zaman bayt
sırası ile sayı sırası sonsuza kadar aynı şeyi söyler. Alt klasörler taranmaz.
Roller göçlerde parolasız yaratılır. Fikstür Roles listesindeki her role
RolePassword parolasını sonradan verir — gerçek dağıtımda bunu dağıtım
betikleri yapar. Listeye yazmadığınız bir rol parolasız kalır ve bağlanamaz.
Havuzlama kapalıdır ve öyle kalmalıdır. Havuzdaki bir bağlantı, test sınıfları arasında konteynerden daha uzun yaşar ve bir sonraki sınıfa ölü bir soket olarak döner.
Türetilmiş fikstür kendi kurulumunu ekleyemez. InitializeAsync ve
DisposeAsync sanal (virtual) değildir. Fikstürün kendi hazırlığı varsa
yapıcıya konur; temizliği varsa sınıf IDisposable gerçekler, çünkü xunit bir
sınıf fikstürünü hem IAsyncLifetime hem IDisposable üzerinden bırakır.
Docker gerekir. Bu paketi kullanan her test takımı çalışan bir Docker ister; paketin kendi testleri de öyle.
Bir ayrıntı: xunit referansı bu projeyi test projesi sanıp paketlemeyi kapatır;
csproj'daki IsPackable=true onu bilerek geri açar.
No packages depend on Bios.Testing.
.NET 10.0
- Dapper (>= 2.1.79)
- Npgsql (>= 10.0.3)
- Testcontainers.PostgreSql (>= 4.13.0)
- xunit (>= 2.9.3)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.10)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.10)
- SSH.NET (>= 2026.0.0)
- Testcontainers (>= 4.13.0)