Etki Alanı Nesnesinin oluşturulmasını test etmek için Birim Testi


11

Şöyle bir Birim Testi var:

[Test]
public void Should_create_person()
{
     Assert.DoesNotThrow(() => new Person(Guid.NewGuid(), new DateTime(1972, 01, 01));
}

Burada bir Person nesnesi oluşturulduğunu, yani doğrulama başarısız olduğunu iddia ediyorum. Örneğin, Rehber null olursa veya doğum tarihi 01/01/1900'den önce ise, doğrulama başarısız olur ve bir istisna atılır (test başarısız olur).

Yapıcı şöyle görünür:

public Person(Id id, DateTime dateOfBirth) :
        base(id)
    {
        if (dateOfBirth == null)
            throw new ArgumentNullException("Date of Birth");
        elseif (dateOfBith < new DateTime(1900,01,01)
            throw new ArgumentException("Date of Birth");
        DateOfBirth = dateOfBirth;
    }

Bu bir test için iyi bir fikir mi?

Not : Etki Alan Modelinin Birim Testi için herhangi bir etkisi varsa Klasikçi bir yaklaşımı izliyorum.


Yapıcı, başlatma işlemi sonrasında iddia edilmeye değer bir mantığa sahip mi?
Laiv

2
Test kurucularını asla rahatsız etmeyin !!! İnşaat düz olmalıdır. Guid.NewGuid () veya DateTime'ın yapıcısında başarısızlık bekliyor musunuz?
ivenxu

@Laiv, lütfen soru güncellemesine bakın.
w0051977

1
Paylaştığınız testin uygulanmasına değmez. Ancak, tam tersini de test ederdim. BirthDate'in hataya neden olduğu durumu test ederim. Kontrol etmek ve test etmek istediğiniz sınıfın değişmezidir.
LAIV

3
Test iyi görünüyor, bir şey için kaydedin: isim. Should_create_person? Bir insan ne yaratmalı? Buna anlamlı bir isim verin Creating_person_with_valid_data_succeeds.
David Arno

Yanıtlar:


18

Bu geçerli bir testtir (oldukça aşırı olsa da) ve bazen yapıcı mantığını test etmek için yapıyorum, ancak Laiv'in yorumlarda belirttiği gibi kendinize nedenini sormalısınız.

Yapımcınız şöyle görünüyorsa:

public Person(Guid guid, DateTime dob)
{
  this.Guid = guid;
  this.Dob = dob;
}

Test edip etmediğini test etmenin çok anlamı var mı? Parametrelerin doğru atanıp atanmadığını anlayabiliyorum, ancak testiniz oldukça fazla.

Ancak, testiniz böyle bir şey yaparsa:

public Person(Guid guid, DateTime dob)
{
  if(guid == default(Guid)) throw new ArgumentException("Guid is invalid");
  if(dob == default(DateTime)) throw new ArgumentException("Dob is invalid");

  this.Guid = guid;
  this.Dob = dob;
}

Daha sonra testiniz daha alakalı hale gelir (kodda bir yere istisnalar attığınız için).

Söyleyeceğim bir şey, genellikle yapıcıda çok fazla mantık olması kötü bir uygulamadır. Temel doğrulama (yukarıda yaptığım boş / varsayılan kontroller gibi) tamam. Ancak veritabanlarına bağlanıyor ve birinin verilerini yüklüyorsanız, kod gerçekten kokmaya başlar.

Bu nedenle, kurucunuz test etmeye değerse (çünkü çok fazla mantık var) o zaman başka bir şey yanlış olabilir.

İş mantığı katmanlarında, kurucularında ve değişken atamalarında bu sınıfı kapsayan diğer testleriniz neredeyse kesinlikle bu testlerden tam olarak ele alınacaktır. Bu nedenle, özellikle kurucu için spesifik testler eklemek anlamsız olabilir. Ancak, hiçbir şey siyah beyaz değildir ve kodları inceliyor olsaydım bu testlere karşı hiçbir şeyim olmazdı - ancak çözümünüzün başka bir yerinde testlerin üstüne ve ötesine çok fazla değer ekleyip eklemediklerini sorgulayacağım.

Örneğinizde:

public Person(Id id, DateTime dateOfBirth) :
        base(id)
    {
        if (dateOfBirth == null)
            throw new ArgumentNullException("Date of Birth");
        elseif (dateOfBith < new DateTime(1900,01,01)
            throw new ArgumentException("Date of Birth");
        DateOfBirth = dateOfBirth;
    }

Sadece doğrulama yapmıyorsunuz, aynı zamanda bir temel kurucu da diyorsunuz. Benim için bu, bu testleri yaptırmak için daha fazla neden sunuyor çünkü kurucu / validasyon mantığı şimdi görünürlüğü azaltan ve beklenmedik değişiklik riskini artıran iki sınıfa ayrılıyor.

TLDR

Bu testlerin bir değeri vardır, ancak doğrulama / atama mantığının çözümünüzdeki diğer testler tarafından kapsanması muhtemeldir. Bu kurucularda önemli testler gerektiren bir sürü mantık varsa, o zaman bana orada gizli bir koku kokusu olduğunu gösteriyor.


@Laith, Lütfen sorumun güncellemesine bakın
w0051977

Örneğinizde bir temel kurucu çağırdığınızı fark ettim. IMHO bu, testinize daha fazla değer katar, kurucu mantık artık iki sınıfa ayrılmıştır ve bu nedenle biraz daha yüksek değişiklik riski taşımaktadır, bu nedenle test etmek için daha fazla neden vermektedir.
Liath

"Ancak, eğer testiniz böyle bir şey yaparsa:" <Bunu kastetmiyorsanız " kurucunuz böyle bir şey yaparsa" ?
Kodos Johnson

"Bu testlerin bir değeri var" - ilginç bir şekilde, benim için ilginç bir şekilde, bu değer, PersonBirthdatedoğum doğrulamasını yapan kişinin dobunu (örn. ) Temsil etmek için yeni bir sınıf kullanarak bu testi gereksiz hale getirebileceğimizi gösteriyor . Benzer şekilde Guidkontrol Idsınıf üzerinde de uygulanabilir . Bu, gerçekte Personyapıcıda bu doğrulama mantığına sahip olmanız gerekmediği anlamına gelir, çünkü nullreferanslar hariç geçersiz verilerle bir tane oluşturmak mümkün değildir . Tabii ki, diğer iki sınıf için testler yazmalısınız :)
Stephen Byrne

12

Zaten burada iyi bir cevap, ama bence ek bir şeyden bahsetmeye değer.

TDD'yi "kitap tarafından" yaparken , kurucu uygulanmadan önce bile kurucuyu çağıran bir test yazmanız gerekir. Bu test, kurucunun uygulamasında sıfır doğrulama mantığı olsa bile, sunduğunuz teste benzeyebilir.

Ayrıca TDD için önce bir başka test yazılması gerektiğini unutmayın.

  Assert.Throws<ArgumentException>(() => new Person(Guid.NewGuid(), 
        new DateTime(1572, 01, 01));

yapıcıya onay eklemeden önceDateTime(1900,01,01) .

TDD bağlamında, gösterilen test mükemmel bir anlam ifade eder.


Güzel açı ben düşünmedim!
Liath

1
Bu bana böyle katı bir TDD formunun neden zaman kaybı olduğunu gösteriyor: test kod yazıldıktan sonra değere sahip olmalı ya da her kod satırını iki kez, bir kez bir iddia olarak ve bir kez kod olarak yazıyorsunuz. Kurucunun kendisinin test edilmesi gereken bir mantık parçası olmadığını iddia ediyorum; "1900'den önce doğan insanlar temsil edilebilir olmamalıdır" iş kuralı test edilebilir ve kurucu bu kuralın uygulandığı yerdir, ancak boş bir kurucu testi ne zaman projeye değer katar?
IMSoP

Kitap gerçekten tdd mi? Ben örnek oluşturmak ve hemen bir kodda yöntemini çağırır. Sonra bu yöntem için test yazmak ve bunu yaparak ben de bu yöntem için örnek oluşturmak gerekir, böylece hem yapıcı hem de yöntem bu sınama kapsamında olacaktır. Yapıcıda bir mantık olmadığı sürece, bu bölüm Liath tarafından kapsanır.
Rafał Łużyński

@ RafałŁużyński: TDD "kitabın yanında" ilk önce test yazmakla ilgilidir . Aslında her zaman önce başarısız bir test yazmak anlamına gelir (sayıları başarısız olarak da derlememek). Bu yüzden ilk önce kurucu olmadığında bile kurucuyu çağıran bir test yazıyorsunuz . Sonra derlemeye çalışın (başarısız olur), sonra boş bir kurucu uygular, derler, testi çalıştırır, sonuç = yeşil. Sonra ilk başarısız testi yazıp çalıştırın - sonuç = kırmızı, daha sonra testi tekrar "yeşil" yapmak için işlevsellik eklersiniz vb.
Doc Brown

Elbette. Önce uygulamayı, sonra test ettiğimi kastetmedim. Ben sadece yukarıdaki bir düzeyde bu kodun "kullanımı" yazmak, sonra bu kodu test, sonra ben uygulamak. Genellikle "TDD Dışı" yapıyorum.
Rafał Łużyński
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.