Testing in .NET #1 — Unit Testing ile Sağlam Temeller Atmak
Test Neden Yazılır?
Testing in .NET #1 — Unit Testing ile Sağlam Temeller Atmak

Test Neden Yazılır?
Yazılım testinin temel amacı kodun çalıştığını kanıtlamak değil, yazılımın beklenen davranışı koruduğuna güven sağlamaktır. Bu yaklaşımla birlikte yazılan kodun request ve response sağlamlığını, güvenilirliğini sağlamak adına test yazmak gerekmektedir. Daha iyi bir deyişle şu şekilde özetlenebilir:
“Bu kodun bugün doğru çalıştığını ve yarın yapılacak değişikliklerden sonra bozulmadığını nasıl garanti ederim?”
Testin ana amacı budur.
Yazılım testinin temel amacı, yazdığımız kodun sadece bugün çalıştığını değil, gelecekte yapılacak değişikliklerden sonra da beklenen davranışı koruduğunu garanti etmektir. Bir geliştirici yeni bir özellik eklediğinde veya mevcut kodu refactor ettiğinde, eski çalışan özelliklerin bozulup bozulmadığını manuel olarak kontrol etmek hem zaman kaybettirir hem de hata yapma riskini artırır. Testler burada devreye girerek bize hızlı geri bildirim sağlar ve kod değişikliklerini daha güvenli hale getirir.
Örneğin bir e-ticaret uygulamasında sipariş oluşturma işlemi ilk başta basit olabilir ancak zamanla indirim, kampanya, stok kontrolü, ödeme ve bildirim gibi yeni kurallar eklendiğinde mevcut davranışların bozulma ihtimali artar; yazılan testler bu eski davranışların korunmasını sağlar.
Testler aynı zamanda refactoring yaparken geliştiriciye güven verir, çünkü kodun iç yapısını değiştirdiğinde dışarıdan beklenen davranışların aynı kaldığını kontrol edebilir. Bunun yanında iyi yazılmış testler yaşayan bir dokümantasyon görevi görür; örneğin CreateOrder_Should_Fail_When_Stock_Is_Not_Enough isimli bir test, sistemin yetersiz stok durumunda sipariş oluşturmadığını açıkça ifade eder ve yeni geliştiricilerin sistemi daha hızlı anlamasına yardımcı olur.
Test yazmak aynı zamanda yazılım tasarımının kalitesini de artırır, çünkü kolay test edilebilen kod genellikle düşük bağımlılığa sahip, sorumlulukları ayrılmış ve daha temiz tasarlanmış koddur. Örneğin bir servis içinde doğrudan veritabanı bağlantısı açmak, mail göndermek ve ödeme yapmak gibi birçok sorumluluğu toplamak test yazmayı zorlaştırırken, bu bağımlılıkları interface'ler aracılığıyla dışarıdan almak sistemi daha esnek ve test edilebilir hale getirir. Kısacası test yazmak, sadece hata bulma yöntemi değil; yazılımın güvenli şekilde gelişmesini, kolay değiştirilebilir olmasını ve ekip içinde daha anlaşılır hale gelmesini sağlayan bir mühendislik pratiğidir.
1.2. Testing Pyramid
Testing Pyramid (Test Piramidi), bir yazılım projesinde hangi tür testlerden ne kadar yazmamız gerektiğini gösteren bir test stratejisidir. İlk olarak Mike Cohn tarafından ortaya atılan bu yaklaşımın temel fikri şudur: Bir projede en fazla hızlı ve düşük maliyetli testler (Unit Test), daha az sayıda ise yavaş ve maliyetli testler (Integration, UI/E2E Test) bulunmalıdır.

Biz bu yazımızda, en çok yazılan, hızlı ve daha az maliyetli bir yaklaşım olan unit test üzerine konuşuyor olacağız.
Unit Test
Unit test, uygulamadaki en küçük test edilebilir parçayı izole şekilde test eder. Genellikle bir class, method veya business kuralı test edilir. Çok hızlı çalışır çünkü herhangi bir database’e gitmez, network kullanmaz veya bir dosya işlemi yapmaz. Amacımız burada, yazmış olduğumuz kodun, doğru bir request ile uygun bir response veriyor mu, kod üzerinde yapmış olduğumuz bir değişiklik ile herhangi bir bozulma yaşanmış mı şeklinde sorulara cevap verebilmektir. Unit Test yazımında dikkat edilen hususlar vardır.
AAA Pattern Nedir?
Test yazımını standartlaştıran, okunabilirliğini arttıran bir tasarım desenidir. 3 adımdan oluşur.
- Arrange (Düzenle): Testin çalışması için gerekli ortamı hazırlar, değişkenleri tanımlar ve test edilecek nesneleri oluşturur.
- Act (Harekete Geç): Test edilecek fonksiyonu veya metodu çalıştırır.
- Assert (Doğrula): Beklenen sonuç ile elde edilen sonucun uyuşup uyuşmadığını kontrol eder.
Test yazımından önce, örnek olması adına ShopHub adında basic bir solution oluşturdum. Burada önce testler olmak üzere, sırasıyla önemli yapıları test edip, kapsamlı bir repo oluşturuyor olacağız.
Unit test ile başlamak adına, basit bir seviyede OrderService.cs dosyası oluşturdum. Burada en basit seviyede sipariş oluşturmak adına bir fonksiyon yer almakta.
public async Task<CreateOrderResponse> CreateOrderAsync(
CreateOrderRequest request,
CancellationToken cancellationToken = default)
Request ve Response olarak beklenen parametreler belli. Burada, kod içerisinde herhangi bir değişiklik yapıldığında dönüşün değişip değişmediğini test etmek gerekiyor. Bunun için istediğimiz senaryolarda, bu değişikliği kontrol edebilmek için test yazmalıyız. Bu projede yazdığımız örnek senaryolar şu şekilde:
[Fact]
public async Task CreateOrder_Should_Create_Order_When_Request_Is_Valid()
...
[Fact]
public async Task CreateOrder_Should_Throw_When_Product_Not_Found()
...
Test sınıfının yazılması için test edilmek istenen sınıfların mock’lanması gerekmektedir. O yüzden şu şekilde bir girizgah yapılmalıdır:
public class OrderServiceTests
{
private readonly Mock<IOrderRepository> _orderRepositoryMock;
private readonly Mock<IProductRepository> _productRepositoryMock;
private readonly OrderService _sut;
public OrderServiceTests()
{
_orderRepositoryMock = new Mock<IOrderRepository>();
_productRepositoryMock = new Mock<IProductRepository>();
_sut = new OrderService(_orderRepositoryMock.Object, _productRepositoryMock.Object);
}...
Request in düzgün olması halinde düzgün bir siparişin oluşturulması için çalışacak test ise şu şekilde yazılabilir:
[Fact]
public async Task CreateOrder_Should_Create_Order_When_Request_Is_Valid()
{
// Arrange
var customerId = Guid.NewGuid();
var productId = Guid.NewGuid();
var product = CreateProduct(productId, price: 25.00m);
_productRepositoryMock
.Setup(repository => repository.GetByIdAsync(productId, It.IsAny<CancellationToken>()))
.ReturnsAsync(product);
var request = new CreateOrderRequest(
customerId,
[new CreateOrderItemRequest(productId, 2)]);
// Act
var response = await _sut.CreateOrderAsync(request);
// Assert
response.Should().NotBeNull();
response.OrderId.Should().NotBeEmpty();
response.CustomerId.Should().Be(customerId);
response.TotalAmount.Should().Be(50.00m);
response.Items.Should().HaveCount(1);
response.Items[0].ProductId.Should().Be(productId);
response.Items[0].Quantity.Should().Be(2);
}
IOrderRepository ve IProductRepositoryinterface'leri mock'lanarak test ortamında kontrol edilebilir hale getirilmiştir. _sut değişkeni ise System Under Test anlamına gelir ve test edilen ana nesneyi temsil eder; burada tüm testlerin odak noktası OrderService sınıfıdır. Yukarıda da eklemiş olduğum CreateOrder_Should_Create_Order_When_Request_Is_Valid, geçerli bir sipariş isteği gönderildiğinde siparişin başarılı şekilde oluşturulduğunu kontrol eder. Arrange bölümünde gerekli test verileri hazırlanır, ürün repository'sinin belirli bir ürün döndürmesi için Moq ile Setup yapılır, Act bölümünde gerçek metot çağrılır ve Assert bölümünde FluentAssertions kullanılarak sonucun beklenen değerlere sahip olup olmadığı doğrulanır. Bu doğrulama XUnit ile de yapılabilir.
[Fact]
public async Task CreateOrder_Should_Throw_When_Product_Not_Found()
{
// Arrange
var productId = Guid.NewGuid();
_productRepositoryMock
.Setup(repository => repository.GetByIdAsync(productId, It.IsAny<CancellationToken>()))
.ReturnsAsync((Product?)null);
var request = new CreateOrderRequest(
Guid.NewGuid(),
[new CreateOrderItemRequest(productId, 1)]);
// Act
var act = () => _sut.CreateOrderAsync(request);
// Assert
await act.Should().ThrowAsync<NotFoundException>()
.WithMessage($"*{productId}*");
}
Yukarıda bulunan ve ikinci test olan CreateOrder_Should_Throw_When_Product_Not_Found, hata senaryosunu test eder; burada repository ürün bulamadığında nulldönecek şekilde ayarlanır ve servisin doğru exception fırlatıp fırlatmadığı kontrol edilir. Bu testte ThrowAsync kullanılması async çalışan metotlarda hata davranışını doğrulamak için kullanılan bir FluentAssertions özelliğidir.
Testlerde kullanılan Arrange-Act-Assert (AAA) pattern, her testin üç aşamalı okunabilir bir yapıya sahip olmasını sağlar: önce gerekli ortam hazırlanır, sonra test edilen işlem çalıştırılır, son olarak sonuç kontrol edilir. CreateProduct yardımcı metodu ise testlerde sürekli aynı Product nesnesini tekrar oluşturmamak için kullanılan bir test helper metodudur ve testlerin daha okunabilir olmasını sağlar. Genel olarak bu sınıf; xUnit ile test organizasyonu, Moq ile bağımlılıkların taklit edilmesi, FluentAssertions ile okunabilir doğrulamalar yapılması, async exception testleri, interaction testing (Verify) ve business rule testleri gibi modern .NET Unit Testing pratiklerini bir arada göstermektedir. Bu yaklaşım sayesinde OrderService üzerinde yapılacak değişikliklerde, sipariş oluşturma davranışının bozulup bozulmadığı hızlı ve güvenilir şekilde kontrol edilebilir.
Biz bu testlerimizde, request i kendimiz oluşturduk. Daha büyük projelerde, işimizi kolaylaştırması adına bir kütüphane de kullanılabilmekte. AutoFixture, .NET testlerinde kullanılan bir test data generation (test verisi üretme) kütüphanesidir. Temel amacı, testlerde ihtiyaç duyduğumuz nesneleri otomatik olarak oluşturmak ve manuel veri hazırlama yükünü azaltmaktır.
Örneğin, normalde request i şu şekilde oluştururuz,
var product = new Product
{
Id = Guid.NewGuid(),
Name = "Test Product",
Price = 25.00m,
StockQuantity = 10,
CreatedAt = DateTime.UtcNow
};
AutoFixture ile birlikte şu şekilde request’ler kendiliğinden oluşabilmektedir.
var fixture = new Fixture();
var product = fixture.Create<Product>();
Buna ek olarak Bogus ile birlikte kullanılırsa, daha gerçeğe yakın veriler üretilebilmektedir.
var faker = new Faker();
var name = faker.Name.FullName();

Modern yazılım geliştirmede test yazmak sadece hata bulmak için kullanılan bir teknik değildir; aynı zamanda yazılımın sürdürülebilirliğini sağlayan önemli bir mühendislik pratiğidir. İyi yazılmış testler geliştiricilere güven verir, refactoring yapmayı kolaylaştırır ve yeni özelliklerin sisteme daha güvenli şekilde eklenmesini sağlar. Bu yazıda testlerin neden gerekli olduğunu, Testing Pyramid yaklaşımını, Unit Test kavramını, AAA Pattern’i, xUnit ile test oluşturmayı, FluentAssertions ile doğrulama yapmayı, Moq ile bağımlılıkları izole etmeyi, AutoFixture ve Bogus ile test verisi üretmeyi konuşmuş olduk. Bu şekilde, diğer testler ve farklı konular ile de çalışmalar yapıp, github reposunda use case’ler olarak ilerlemeye devam edeceğiz.
Repoya buradan ulaşabilirsiniz.
Okuduğunuz için teşekkürler!
메타데이터
- post_id
- a376ebec4605
- slug
- testing-in-net-1-unit-testing-ile-sağlam-temeller-atmak-a376ebec4605
- url
- https://medium.com/@olmezsude/testing-in-net-1-unit-testing-ile-sa%C4%9Flam-temeller-atmak-a376ebec4605
- canonical_url
- https://medium.com/@olmezsude/testing-in-net-1-unit-testing-ile-sa%C4%9Flam-temeller-atmak-a376ebec4605
- author_url
- https://medium.com/@olmezsude
- status
- ok
- fetched_at
- 2026-07-21 11:16:06