Ana içeriğe geç

Test Best Practices

Test best practices, güvenilir, okunabilir ve sürdürülebilir test süitleri oluşturmayı sağlar; kötü pratikler bakım yükü ve güven kaybına yol açar.


1. Flaky (Güvenilmez) Test Yazmak

Yanlış Kullanım: Zamana veya sıraya bağımlı test yazmak.

[Fact]
public void GetRecentOrders_ReturnsLastHourOrders()
{
    var order = new Order { CreatedAt = DateTime.Now.AddMinutes(-30) };
    _context.Orders.Add(order);
    _context.SaveChanges();

    var result = _service.GetRecentOrders();

    Assert.Single(result); // Gece yarısı çalışırsa başarısız olabilir
}

İdeal Kullanım: Zamanı kontrol altına alarak deterministik test yazın.

[Fact]
public void GetRecentOrders_ReturnsLastHourOrders()
{
    var now = new DateTime(2024, 6, 15, 14, 0, 0, DateTimeKind.Utc);
    var clock = new FakeClock(now);
    var order = new Order { CreatedAt = now.AddMinutes(-30) };
    _context.Orders.Add(order);
    _context.SaveChanges();

    var service = new OrderService(_context, clock);
    var result = service.GetRecentOrders();

    Assert.Single(result);
}

2. Magic Number Kullanmak

Yanlış Kullanım: Anlamı belirsiz sabit değerler kullanmak.

[Fact]
public void Test()
{
    var result = _calculator.Calculate(100, 0.18m, 50, true);
    Assert.Equal(168.2m, result);
}

İdeal Kullanım: Değişkenlerle anlamı açıkça belirtin.

[Fact]
public void Calculate_WithTaxAndShippingForPremiumCustomer_AppliesFreeShipping()
{
    const decimal basePrice = 100m;
    const decimal taxRate = 0.18m;
    const decimal shippingCost = 50m;
    const bool isPremium = true;
    const decimal expectedTotal = 118m; // Premium = ücretsiz kargo

    var result = _calculator.Calculate(basePrice, taxRate, shippingCost, isPremium);

    Assert.Equal(expectedTotal, result);
}

3. FluentAssertions Kullanmamak

Yanlış Kullanım: Assert mesajlarının hata durumunda anlamsız olması.

[Fact]
public void GetActiveUsers_ReturnsOnlyActiveUsers()
{
    var users = _service.GetActiveUsers();

    Assert.Equal(3, users.Count);
    Assert.True(users.All(u => u.IsActive));
    Assert.Contains(users, u => u.Name == "Ali");
}
// Hata mesajı: "Assert.Equal() Failure. Expected: 3, Actual: 2"

İdeal Kullanım: FluentAssertions ile okunabilir assert’ler yazın.

[Fact]
public void GetActiveUsers_ReturnsOnlyActiveUsers()
{
    var users = _service.GetActiveUsers();

    users.Should().HaveCount(3)
        .And.OnlyContain(u => u.IsActive)
        .And.Contain(u => u.Name == "Ali");
}
// Hata mesajı: "Expected collection to contain 3 item(s), but found 2."

4. Test Coverage Takıntısı

Yanlış Kullanım: %100 coverage için anlamsız testler yazmak.

[Fact]
public void Constructor_SetsProperties()
{
    var dto = new ProductDto { Id = 1, Name = "Test" };

    Assert.Equal(1, dto.Id);
    Assert.Equal("Test", dto.Name);
}

[Fact]
public void ToString_ReturnsString()
{
    var dto = new ProductDto { Name = "Test" };
    Assert.NotNull(dto.ToString());
}

İdeal Kullanım: İş mantığı ve kritik yolları test edin, getter/setter test etmeyin.

// DTO getter/setter testleri gereksiz
// Bunun yerine gerçek iş mantığını test edin

[Fact]
public void ApplyDiscount_WhenDiscountExceedsPrice_ThrowsDomainException()
{
    var order = new Order { TotalPrice = 100 };

    Assert.Throws<DomainException>(() => order.ApplyDiscount(150));
}

[Fact]
public void ChangeStatus_FromShippedToCancelled_ThrowsDomainException()
{
    var order = new Order { Status = OrderStatus.Shipped };

    var ex = Assert.Throws<DomainException>(() => order.Cancel());
    Assert.Equal("Kargolanmış sipariş iptal edilemez.", ex.Message);
}

5. Shared State ile Test Kirliliği

Yanlış Kullanım: Statik veya paylaşılan state kullanmak.

public class DiscountTests
{
    private static readonly DiscountService _service = new();
    private static decimal _lastResult;

    [Fact]
    public void Test1()
    {
        _lastResult = _service.Calculate(100);
        Assert.Equal(90, _lastResult);
    }

    [Fact]
    public void Test2()
    {
        Assert.Equal(90, _lastResult); // Test1'e bağımlı
    }
}

İdeal Kullanım: Her test kendi instance’larını oluştursun.

public class DiscountTests
{
    private readonly DiscountService _service;

    public DiscountTests()
    {
        _service = new DiscountService(); // Her test için yeni instance
    }

    [Fact]
    public void Calculate_WithStandardDiscount_ReturnsTenPercentOff()
    {
        var result = _service.Calculate(100);
        Assert.Equal(90, result);
    }

    [Fact]
    public void Calculate_WithZeroAmount_ReturnsZero()
    {
        var result = _service.Calculate(0);
        Assert.Equal(0, result);
    }
}