C#'ta Birim Testlerinde Fluent Assertions
[[academy-video-youtube({"vid": "TytferBCLOo", "start_time": "0", "title": "Fluent Assertions in Unit Testing in C#", "creator": "Tim Corey", "length": "10m 6s"})]]
Birim testleri bir kez yazılır ve birçok kez okunur, bu da assertion satırını bir test suite'inde en önemli detaylardan biri yapar. Assert.Equal(expected, actual) çalışır, ancak sadece iki stringi karşılaştıran bir hata mesajı her zaman testin neyi kontrol ettiğini açıklamaz. Fluent Assertions, iddia söz dizimini, satırın uyguladığı kuralı okumasını sağlayacak şekilde yeniden şekillendirir ve hata mesajı bir fark yerine bir cümle haline gelir.
Tim Corey, "C#'de Birim Testlerinde Fluent Assertions" adlı videosunda, zaten bir SampleClass mutlu yolu uygulayan bir xUnit projesi alır, ardından basit kelime aralarına bölme mantığını bozan bir sınır durumu (Eddie Van Halen) tanıtır. Fluent Assertions paketini yükler, .Should().Be() zincirini kullanarak assertion'ı yeniden yazar, bir tek değer üzerinde çoklu koşulları zincirlemenin nasıl yapılacağını gösterir ve tek bir testin tüm başarısız koşulları bir kerede raporlayacağı AssertionScope ile bitirir, ilkinde durmak yerine. Test paketi, çıplak eşitlik farklarının net okunamayacak kadar büyümüş olan ekipler burada yükseltme yolunu bulacak.
xUnit Başlangıç Noktası
[0:35 - 2:46] Ekrandaki proje küçük: kurucusuna tam bir isim alan ve boşluk üzerinde FirstName ve LastName olarak bölen tek bir SampleClass sınıf kütüphanesi artı mutlu yolu uygulayan bir xUnit test projesi. Testler iki girişe karşı aynı mantığı çalıştırmak için InlineData attribute'ları ile Theory kullanır ("Tim Corey" ve "Sue Storm"), ve her assertion standart Assert.Equal(expected, actual) formundadır.
[Theory]
[InlineData("Tim Corey", "Tim")]
[InlineData("Sue Storm", "Sue")]
public void TestFirstNameProperty(string fullName, string expected)
{
var sample = new SampleClass(fullName);
Assert.Equal(expected, sample.FirstName);
}[Theory]
[InlineData("Tim Corey", "Tim")]
[InlineData("Sue Storm", "Sue")]
public void TestFirstNameProperty(string fullName, string expected)
{
var sample = new SampleClass(fullName);
Assert.Equal(expected, sample.FirstName);
}Suit çalıştırmak altı geçen testi verir. Bunda yanlış bir şey yoktur ve 'ilk boşluk son' modeline uyan durumlar için, iddialar yeterince net okunur. İlginç alan, bir ismin o yapıya uymadığı durumda başlar.
Mutlu Yolun Çalışmayı Bıraktığı Yerde
[2:46 - 4:32] Tim'in seçtiği köşe durumu 'Eddie Van Halen' olup, soyadının 'Van Halen' olduğu üç kelimeli bir addır, sadece ilk boşluktan sonraki belirteç değil. Mevcut uygulama ayrılmış indeks 1'i alır ve bunu soyadı olarak çağırır, bu yüzden sınıf soyadı için 'Van' döndürür ve 'Halen'i tamamen bırakır. Hata gerçektir ve bunun için başarısız bir test yazmak, onu düzeltmenin ilk adımıdır.
Assert.Equal("Van Halen", sample.LastName) ile o testi yazmak çalışır, ancak hata mesajı bağlamsız bir string karşılaştırması olarak okunur. Fluent Assertions'a geçmek, aynı testin kuralı sözlerle ifade etmesini ve testi yapılan özelliği adlandıran bir hata mesajı üretmesini sağlar. Küçük bir suite için fark görünüşte önemsiz görünse de; birkaç yüz iddiadan oluşan bir suite için okunabilirlik farkı birikir.
Fluent Assertions Kurulumu
[4:32 - 5:10] Paket, FluentAssertions altında NuGet üzerinde bulunmaktadır. Tim buna, istatistiksel olarak herhangi bir yeni katılımcının bunu daha önce görmüş olma olasılığını artıran, kayıttaki en çok indirilen paketlerden biri olarak işaretler. Test projesi üstte iki using direktifi alır:
using FluentAssertions;
using FluentAssertions.Execution;using FluentAssertions;
using FluentAssertions.Execution;İlki, çoğu testin ihtiyaç duyduğu iddia genişletme yöntemlerini getirir. İkincisi daha sonra videoda bahsedilen AssertionScope türü için mevcuttur; iddiaları gruplamayan testler bunu dışarıda bırakabilir.
Should.Be Sözdizimi
[5:10 - 6:14] Van Halen testinin minimum Fluent Assertions yeniden yazımı:
[Fact]
public void TestEdgeCaseNames()
{
var sample = new SampleClass("Eddie Van Halen");
sample.LastName.Should().Be("Van Halen");
}[Fact]
public void TestEdgeCaseNames()
{
var sample = new SampleClass("Eddie Van Halen");
sample.LastName.Should().Be("Van Halen");
}Zincir, konuşulan İngilizceye yakındır. .Should() uzantısı, karşılaştırmayı tanımlayan yöntemlere sahip bir assertion nesnesi döndürür; .Be(...) bir eşitlik kontrolü yapar. Büyük/küçük harf duyarlılığı, ön boşluk ve arka boşluk, üretim kodundaki çoğu dize karşılaştırmasının davranışına uyan bu kontrolün bir parçasıdır.
Bozuk uygulamaya karşı bu testi çalıştırmak, özelliği adlandıran ve boşluğu açıklayan bir hata mesajı üretir: "Örnek.LastName'in 'Van Halen' ve uzunluğunun 9 olması bekleniyordu, ancak 'Van'ın uzunluğu 3." Mesaj, teste tabi değeri üreten ifade ile tanımlar, bu, çıplak Assert.Equal formunun sağlamadığı türdendir.
Birden Fazla Koşulu Zincirlemek
[6:14 - 8:10] Tek bir özellik sıklıkla birden çok kuralı karşılamalıdır. Fluent Assertions, zincirin tek bir ifadede kalması için .And ile koşulları birleştirir:
sample.LastName.Should()
.StartWith("Van")
.And.EndWith("len")
.And.Contain(" ");sample.LastName.Should()
.StartWith("Van")
.And.EndWith("len")
.And.Contain(" ");Zincirdeki her bağlantı ayrı bir koşuldur. Varsayılan olarak zincir ilk hatada raporlamayı durdurur: StartWith("Van") geçer ama EndWith("len") başarısız olursa, hata mesajı EndWith adını verir ve assertion orada durur. Her koşulun bağımsız olduğu ve her başarısızlık yüzeye sunulmasını istediğiniz testler için, iddia kapsamı (bir sonraki bölüm) o davranışı değiştirir.
Zincirleme sözdizimi, eşitliği saymaktan ziyade niyeti ifade etmeye yöneliktir. 'Soyadı Van ile başlamalı, len ile bitmeli ve bir boşluk içermeli' diyen bir test bir spesifikasyon gibi okunur. Aynı mantık üç ayrı Assert.True çağrısıyla yazıldığında, aralarında bağlantı kurulmadan üç boolean kontrolü olarak okunurdu.
Her Başarısızlığı İddia Kapsamları ile Rapor Etme
[8:10 - 9:34] İleri koşulların öncekilere bağlı olduğu zincirler için, varsayılan davranış olan ilk başarısızlıkta durmak uygundur. Her koşulun bağımsız olarak önemli olduğu zincirler için, assertion'ları bir AssertionScope içinde sarmak test raporunu tek bir çalıştırmada her başarısızlığı bildirir hale getirir:
[Fact]
public void TestEdgeCaseNames()
{
var sample = new SampleClass("Eddie Van Halen");
using var _ = new AssertionScope();
sample.LastName.Should().StartWith("Van");
sample.LastName.Should().EndWith("len");
sample.LastName.Should().Contain(" ");
}[Fact]
public void TestEdgeCaseNames()
{
var sample = new SampleClass("Eddie Van Halen");
using var _ = new AssertionScope();
sample.LastName.Should().StartWith("Van");
sample.LastName.Should().EndWith("len");
sample.LastName.Should().Contain(" ");
}Using-var-discard satırı standart patern: kapsam, değişken yöntem sonunda kapsam dışına çıkana kadar sürer ve atama alt çizgisi, değişkenin kendisinin asla okunmadığını belirtir. Test başarısız olduğunda, mesaj ayrı bir satır olarak her başarısız koşulu içerir, bu da bir çalışmanın üç sorunu yüzeye çıkarmak yerine tüm hikayeyi vermesi anlamına gelir. Birçok özellik üzerinden döndürülen bir nesnenin şeklini doğrulayan testler için, bu, döngü başına bir hata düzeltmek ile hepsini bir kerede düzeltmek arasındaki farktır.
Sonuç: Spesifikasyonlar Gibi Okunan Testler
[9:34 - 10:06] Fluent Assertions, testlerin neyi doğruladığını değiştirmez; doğrulamanın nasıl okunduğunu değiştirir. .Should() zinciri, .And birleşimi ve AssertionScope gruplandırması, test kodunu teste tabi davranışın yazılı bir spesifikasyonuna daha da yaklaştırır.
Sonuç
[9:34 - 10:06] Test projesine Fluent Assertions eklemek bir NuGet yükleme, iki using bildirgesi ve assertion hattının Assert.Equal(expected, actual)'den actual.Should().Be(expected)'ya yeniden yazımını gerektirir. Zincirler çok koşullu kuralları tek bir ifadede ifade eder ve AssertionScope, ilkinde durmak yerine tek bir testin her hatayı raporlamasını sağlar. Bu, bozuk kuralı açıklayan hata mesajlarının, bir yığın izleme ile bir cümle arasındaki farkıdır.
Örnek İpucu: Koleksiyonlar üzerinde assert yaparken, property-property karşılaştırması yerine result.Should().BeEquivalentTo(expected) tercih edin. Sizin için nesne grafiğini dolaşır ve yol ile ilk farklı özellik raporlar (örneğin, users[2].Address.City), bu, bir 50 elemanlık liste bir iç içe alan üzerinde anlaşmadığında "koleksiyonlar eşit değil" mesajından çok daha kullanışlıdır.
Tam videoyu YouTube'daki Kanalında izleyin ve 10 Dakikalık Eğitim serisinde C# testlerini okunabilir, sürdürülebilir hale getirme konusunda daha fazla fikir edinin.

