> For the complete documentation index, see [llms.txt](https://bekoturko.gitbook.io/yazilim-gelistirme-wiki/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bekoturko.gitbook.io/yazilim-gelistirme-wiki/test-gelistirme/birim-testleri/birim-testi-gelistirme-prensipleri.md).

# Birim Testi Geliştirme Prensipleri

Birim Testi Geliştirme Prensipleri

### Tanım

Bu belge birim testi geliştirirken gözetilmesi ve uygulanması gereken prensipleri içerir.

### Genel Prensipler

* Her sınıf için ayrı bir test dosyası oluşturmak doğru olacaktır. (*Fixture-Per-Class:* Her sınıfın bir test sınıfının olduğu geliştirme yöntemi)
* Testler *Dokümantasyon Olarak Test (Test as Documentation)* amacıyla da oluşturulur.
* Testler test edilemeyen kodlar olduğu için testler olabildiğince basit tutulmalı, tekrarlanan veya karmaşık işlemler *Yardımcı Metotlar* veya *Test Utility Method* lara taşınmalı, bu kodlar test edilmelidir.
* **Kod tekrarı** ndan kaçınılmalı, bu durumlarda *Yardımcı Metotlar* veya *Test Utility Method* lar kullanılmalı, bu kodlar test edilmelidir.

**Elle Verilen Değerler**

* Kurulumlarda veya doğrulamalarda kullanılan değerler test içinde elle verilmemeli, yardımcı metotlar kullanılmalıdır.
* Bu gibi değerlerin fazla olması testi belirsiz ve karmaşık hale getirir. *Setup* ile *Assert* arasındaki neden-sonuç ilişkinin okunup anlaşılamamasına neden olur.

**Belirsiz Test (Obscure Test)**

* *Belirsiz Test (Obscure Test)* geliştirmekten kaçınılmalıdır. Bu tip testler aşağıdaki gibi belirtilere sahiptir:
  * Anlaşılmaz isimlendirmeler
  * Fazla kod
  * Koşullu mantıklar
* *Belirsiz Test (Obscure Test)*
  * *Dokümantasyon Olarak Test (Test as Documentation)* niteliğinin kaybedilmesine neden olur.
  * Bakımı zordur.
  * Test hatası veya uygulama kodu hatası oluşmasına zemin hazırlarlar.

### Her Koşul = Yeni Test

* Her birim testi **sadece bir koşulu** test etmelidir.
* Testlerde koşullu ifade mantıkları (`if`, `switch`, vb. kullanılmamalıdır.)
* `if`, `switch`, `for`, `foreach` blokları testleri karmaşıklaştırır. Farklı durumların kendine özel testleri olmalıdır.
* Koşullu ifade kullanmak yerine *Guard Assertion* kullanılabilir. (Örneğin bir nesnenin null olmaması gerekiyorsa bunun için `if` yazmak yerine `Should().NotBeNull()` kullanılır.)

### Test Kodu = Uygulama Kodu

* Uygulama kodu refactor edildiği gibi test kodu da yeniden düzenlenmeli (refactor) ve temiz olmalıdır.
* Testlerin *Dokümantasyon Olarak Test (Test as Documentation)* niteliği kazanması ve bu niteliği koruması için temiz ve basit olması gerekir.

*Clean Code - Birim Testleri*

**Testleri Temiz Tutmak**

* Uygulama kodu değişince testler de değişmelidir. Testler ne kadar kirliyse, değişmeleri de o kadar zor olur. Geliştirmelerin uzamasını testlere bağlayan geliştiriciler test yazmayı bırakırlar. Bu da hata oranın artmasına ve kodun istenmeyen şekilde gelişmesine (karmaşıklaşmasına, temizliğini yitirmesine) neden olur. Uygulama kodu zamanla çürümeye başlar.
* Test kodu uygulama kodu kadar önemlidir.
* Kodumuzu esnek, bakım yapılabilir ve yeniden kullanılabilir yapan şey birim testleridir.

**Testleri Temiz Yapan Şey: Okunabilirlik**

* Açıklık (clarity)
* Basitlik (simplicity)
* İfadenin yoğunluğu, özlüğü (daha az ifadeyle daha çok şey anlatmak.)
* Testler için alana özel bir dil geliştirmek

### Arrange (Setup)

* Test kurulumlarının yapıldığı bölümlerde tekrar eden kurulum kodları için *Niyet Belirten İsimler* e sahip *Creation Method* lar oluşturulmalı.
* Mock nesneler `StrictMock` olarak tanımlanmalı.
* Verify edilecek mock metot çağrılarının kurulumları `Verifiable()` olarak işaretlenmeli.

#### Strict Mock

bkz. [Strict Mock](/yazilim-gelistirme-wiki/test-gelistirme/birim-testleri/strict-mock.md)

### Assert

Testin en önemli bölümü *Assert* bölümüdür. Bu bölümde, beklenen tüm davranışlar ve ilgili çıktılar kontrol edilmeli ve doğrulanmalıdır.\
Eksik bırakılan Assertleri olan bir testin hiç olmaması daha iyidir, çünkü kod kapsama oranının tamamlanmasına neden olur ve eksik bırakılan doğrulamalar unutulur.**Kod Kapsama Oranı**

* Kod kapsamının %100 veya daha fazla olması bir şey ifade etmez. (Önemli olan beklenen tüm davranışların doğrulanmasıdır.)
* Kod kapsama oranı %20 ise bir şey ifade eder. (Birçok testin eksik olduğunu)

**Kod Tekrarı**\
Birkaç testte tekrar eden veya bir - iki fazla satırdan uzun olan Assert tanımları *Niyet Belirten İsimler\_e sahip \_Custom Assertion* metotlarına çevrilmeli.

**Beklenen Nesne (Expected Object)**\
Assert işlemlerinde kontroller *Beklenen Nesne (Expected Object)* ile yapılmalı. (FluentAssertions `Should().BeEquivalentTo()` metodu bu amaç için uygun.)**Doğrulama (Verify)**

* Sonuç döndürmeyen (void) metotların davranışları `Verify` ile doğrulanmalı.
* Doğrulanacak mock metot çağrıları `Verify()` metodu ile doğrulanmalı.

### Yardımcı Metotlar (Helper Method)

* Yardımcı metotlar:
  * Niyet belirten isimlere sahip olmalıdır.
  * Test edilebilirler. (ve edilmelidirler.)
  * Yeniden kullanılabilirler.
* `Creation Method` ve `Custom Assertion` gibi metotlar testlerde kullanılan yardımcı metotlardır.
* Yardımcı setup metotları aşağıdaki tiplerde olabilir:
  * Creation Method: Nesne oluşturmak için kullanılır.
  * Parameterized Creation Method: Farklı koşulları oluşturabilmek için kullanılacak değerlerin parametre olarak geçirildiği nesne oluşturma metotlarıdır.
  * Anonymous Creation Method: Nesne değerlerinin önemli olmadığı, sadece nesnenin herhangi bir örneğini oluşturup döndüren nesne oluşturma metotlarıdır.

#### Yardımcı Metotların Organizasyonu

* Bu metotlar sadece belli bir sınıfın test fixture'ında kullanılıyorsa:
  * o sınıfın içinde private olarak tanımlanabilir
  * o sınıfın dosyasında ayrı bir sınıf olarak tanımlanabilir.
  * Test projesinde aşağıdaki gibi bir dizin yapısıyla yönetilebilir. (Bu seçenek önerilir.)
    * `Helpers/SetupHelpers`
    * `Helpers/AssertHelpers`
* Yardımcı metotların testleri de yukarıda önerilen bir dizin yapısıyla yönetilebilir.
  * `HelperTests/SetupHelperTests`
  * `HelperTests/AssertHelperTests`

### Örnek Test (Pseudo-Code)

```csharp
[Test]
public void MethodName_Condition_Behaviour()
{
    //Arrange
    SetupFooForCondition();

    //Act
    var result = sut.DoSomething();

    //Assert
    AssertExpectedBehaviourForCondition();
}
```

### Clean Code - Birim Testleri

**Testlerin sahip olması beklenen özellikler: F.I.R.S.T.**

* Fast
  * Testler hızlı çalışmalı. Hızlı olmazsa çalıştırmazsın.
  * Sorunları önceden göremezsin. Mikro geribildirim şansını kaçırırsın.
  * Kodu temizlemek için özgür hissetmezsin, çünkü kodu düzenlerken testleri sık sık çalıştırıp her adımda yeni durumu doğrulaman gerekir. Kodu temizlemediğin için kod çürür.
* Independent
  * Testler birbirlerinden bağımsız olmalı. Çalışma sıraları veya ortak kullandığı veriler (örneğin kurulum verileri) sonuçlarını etkilememeli.
* Repeatable
  * Ortamdan bağımsız olarak çalıştırılabilmeli, tekrar edilebilmeli.
* Self-Validating
  * Boolean bir sonucu olmalı. Kendini doğrulamalı.
  * Geçmeli veya geçmemeli.
* Timely
  * Doğru zamanda yazılmalı. (Uygulama kodundan hemen önce)
  * Uygulama kodundan sonra yazarsan uygulama kodunun test edilmesinin zor olduğunu görebilirsin.
  * Uygulama kodunu test edilebilir olarak tasarlamamış olabilirsin.

**Sonuç:**\
Testlerin çürümesine izin verirsen uygulama kodu da çürür. Testleri temiz tutmak gerekir.

### One Bad Attribute

Bir nesnenin geçerlilik kontrolleri yapıldığında sadece bir özelliğinin geçersiz olma durumları test edilebilir. *One Bad Attribute* bu durumlarda kullanılması önerilen bir pratiktir.

* Önce nesne geçerli olarak oluşturulur.
* Geçerli olmaması istenen özellik geçersiz hale getirilir.
* Nesne test edilir.

Birçok özelliğinin ayrı ayrı geçerlilik kontrolleri yapılacak nesnelerde kullanışlı bir çözüm sunar. Kurulum metodu olarak sadece geçerli nesne oluşturma metodu kullanılır ve kod tekrarı oluşmaz.
