Birim testleri ile ne amaçladığımıza geçmiş bir yazıda bakmış ve Unit Testing Principles, Practices, and Patterns kitabından aşağıdaki tanım ile bitirmiştim:
Bir geliştiricinin, yaptığı geliştirmenin beklediği gibi çalıştığından emin olmak amacıyla yazdığı aşağıdaki özelliklere sahip test türüdür:
- Otomatik çalıştırılabilir
- Kodun küçük bir parçasını (birim olarak da bilinir) doğrular
- Hızlı çalışır
- İzole çalışır
Bu tanım her ne kadar açık görünse de birim kavramını iki farklı şekilde yorumlamak mümkün – klasik bakış ve taklitçi (mockist) bakış.
Taklitçi (mockist) bakış birim kavramını izole edilmiş bir kod parçası – tek bir fonksiyon, bir sınıf, bir tip – olarak yorumlarken; klasik bakış birimi kod değil bir sistem davranışı olarak görüyor. Gelin iki yoruma biraz daha detaylı bakalım.
Taklitçi (Mockist) okul
Londra okulu olarak da bilinen bu grup; test esnasında birimin izole olması konusunda katıdır. Bu gruba göre bir birimin bütün bağımlılıkları test dublörleri ile değiştirilmeli ve birim bütün dış etkenlerden bağımsız şekilde test edilmelidir. Bu test tarzını Martin Fowler münzevi (solitary) testler olarak isimlendiriyor.

Aşağıda Unit Testing Principles, Practices, and Patterns kitabından bir örnek var. İki detaya dikkatinizi çekmek istiyorum:
Storesınıfı yerine ikame olarakMock<IStore>kullanılıyor- Doğrulama
storeMock.Verifyile ikamenin üzerinde çağrı kontrolü ile yapılıyor
public class Mockist
{
[Fact]
public void Purchase_succeeds_when_enough_inventory()
{
// Arrange
var storeMock = new Mock<IStore>();
storeMock
.Setup(x => x.HasEnoughInventory(Product.Shampoo, 5))
.Returns(true);
var customer = new Customer();
// Act
bool success = customer.Purchase(
storeMock.Object, Product.Shampoo, 5);
// Assert
Assert.True(success);
storeMock.Verify(
x => x.RemoveInventory(Product.Shampoo, 5),
Times.Once);
}
[Fact]
public void Purchase_fails_when_not_enough_inventory()
{
// Arrange
var storeMock = new Mock<IStore>();
storeMock
.Setup(x => x.HasEnoughInventory(Product.Shampoo, 5))
.Returns(false);
var customer = new Customer();
// Act
bool success = customer.Purchase(
storeMock.Object, Product.Shampoo, 5);
// Assert
Assert.False(success);
storeMock.Verify(
x => x.RemoveInventory(Product.Shampoo, 5),
Times.Never);
}
}
Klasik okul (Detroit okulu)
Bu bakış açısı birimi bir sistem davranışı olarak gördüğünden test yaklaşımları da sosyal (sociable) testler yazmak yönünde. Sosyal testler; kodu izole etmek yerine bir davranışı bütün dahili parçaları kullanarak sistemin sınırlarına kadar test etmeye çalışır.
Bu bakış açısında test dublörü kullanımı sınırlıdır. Sistem sınırlarında; veritabanı, dosya sistemi gibi dış sistemler ikameleri ile değiştirilir.

Aşağıda aynı testlerin sosyal versiyonunu bulabilirsiniz. Burada;
- Taklitçi yaklaşımdan farklı olarak
Storesınıfı olduğu gibi kullanılır - Doğrulama
Storesınıfının üzerinde değer kontrolü ile yapılır
public class Classical
{
[Fact]
public void Purchase_succeeds_when_enough_inventory()
{
// Arrange
var store = new Store();
store.AddInventory(Product.Shampoo, 10);
var customer = new Customer();
// Act
bool success = customer.Purchase(store, Product.Shampoo, 5);
// Assert
Assert.True(success);
Assert.Equal(5, store.GetInventory(Product.Shampoo));
}
[Fact]
public void Purchase_fails_when_not_enough_inventory()
{
// Arrange
var store = new Store();
store.AddInventory(Product.Shampoo, 10);
var customer = new Customer();
// Act
bool success = customer.Purchase(store, Product.Shampoo, 15);
// Assert
Assert.False(success);
Assert.Equal(10, store.GetInventory(Product.Shampoo));
}
}
public enum Product
{
Shampoo,
Book
}
Birkaç cümle ile iki yaklaşımı karşılaştıralım;
- Taklitçi yaklaşımda testlerin başarısız olması durumunda problemi tespit etmek kolaydır çünkü kısıtlı bir kod test edilmiştir. Klasik yaklaşımda Store sınıfında yaptığımız bir hata alakalı alakasız çoğu testin başarısız olmasına sebep olacaktır.
- Taklitçi yaklaşımın görece daha hızlı çalıştığı söylenebilir ama taklit için kullandığınız kütüphanelerin performansı belirleyici olacaktır.
- Klasik yaklaşım disiplin ve bilgi birimi gerektirir. Taklitçi yaklaşım görece daha kolay anlatılır ve uygulanır – her sınıf için bir test sınıfı
- Taklitçi yaklaşım koda çok bağımlıdır. Kodda yapılan değişiklikler testleri de değiştirmeye sebep olur. Klasik yaklaşım ise davranışa yani çözülen probleme bağımlı olduğundan anca problem tanımı değişirse testlerde değişiklik gerektirir.
Ben bu son maddeyi çok kritik buluyorum. Uygulamanın kolay olmasından ötürü taklitçi yaklaşım çok yaygın ve bu yaklaşımda birim testleri faydadan çok kod ile beraber bakım gerektiren maliyetlere dönüşüyor. Büyük taklit(mock) kütüphanelerinin kullanıldığı okuması zor karmaşık yapılar haline geliyor.
Eğer siz taklitçi gruptansanız birim testlerini ne amaçla yazdığımızı tekrar değerlendirin ve klasik yaklaşıma bir şans verin derim.

