Bir hikayeyi anlatmak için birim testleri kullanmak iyi bir fikir mi?


13

Bu yüzden, bir süre önce yazdığım bir doğrulama modülüm var. Şimdi yolumdaki hataları görüyorum ve bunun için birim testleri yazıyorum. Birim testleri yazarken, iyi isimler ve test edilecek iyi alanlar bulmakta zorlanıyorum. Mesela benim gibi şeylerim var

  • RequiresLogin_should_redirect_when_not_logged_in
  • RequiresLogin_should_pass_through_when_logged_in
  • Login_should_work_when_given_proper_credentials

Şahsen, "uygun" görünse de biraz çirkin olduğunu düşünüyorum. Ben de sadece onları tarayarak testler arasında ayrım yapmakta sorun var (ben sadece neyin başarısız olduğunu bilmek için yöntem adını en az iki kez okumak zorunda)

Bu yüzden, belki de sadece işlevselliği test eden testler yazmak yerine, senaryoları kapsayan bir dizi test yazacağımı düşündüm.

Örneğin, bu bir test saplama olduğunu:

public class Authentication_Bill
{
    public void Bill_has_no_account() 
    { //assert username "bill" not in UserStore
    }
    public void Bill_attempts_to_post_comment_but_is_redirected_to_login()
    { //Calls RequiredLogin and should redirect to login page
    }
    public void Bill_creates_account()
    { //pretend the login page doubled as registration and he made an account. Add the account here
    }
    public void Bill_logs_in_with_new_account()
    { //Login("bill", "password"). Assert not redirected to login page
    }
    public void Bill_can_now_post_comment()
    { //Calls RequiredLogin, but should not kill request or redirect to login page
    }
}

Bu bir örüntü duyuldu mu? Kabul öyküleri gördüm, ama bu temelde farklı. En büyük fark, testleri "zorlamak" için senaryolar bulmam. Test etmem gereken olası etkileşimleri manuel olarak bulmaya çalışmak yerine. Ayrıca, bunun tam olarak bir yöntem ve sınıfı test etmeyen birim testlerini teşvik ettiğini biliyorum. Bence bu tamam. Ayrıca, genellikle testlerin birbirinden bağımsız olduğunu ve siparişin önemli olmadığını (bu durumda nerede olacağını) varsaydıklarından, bunun en azından bazı test çerçeveleri için sorunlara neden olacağının farkındayım.

Her neyse, bu tavsiye edilebilir bir kalıp mı? Veya bu, "birim" testleri yerine API'mın entegrasyon testleri için mükemmel bir seçim olabilir mi? Bu sadece kişisel bir projede, bu yüzden iyi gidebilecek veya gelmeyebilecek deneylere açığım.


4
Ünite, entegrasyon ve fonksiyonel testler arasındaki çizgiler bulanıktır, eğer test koçanıza bir isim seçmek zorunda kalırsam, bu işlevsel olacaktır.
yannis

Bence bu bir zevk meselesi. Şahsen _testekli ile test ettiğim şeyin adını kullanıyorum ve beklediğim sonuçları not etmek için yorumları kullanıyorum. Kişisel bir proje ise, kendinizi rahat hissettiğiniz bir tarz bulun ve buna bağlı kalın.
Bay Lister

1
Ben Yerleştir / Act / Assert desen kullanarak birim testleri yazma daha geleneksel yolda ayrıntıları ile bir cevap yazdım ama arkadaş kullanarak çok başarılı olmuştur github.com/cucumber/cucumber/wiki/Gherkin olduğunu spec ve afaik için kullanılan salatalık testleri üretebilir.
StuperUser

Nunit veya benzeri bir yöntemle gösterdiğiniz yöntemi kullanmasam da, nspec daha fazla hikaye odaklı bir yapıda bağlam oluşturma ve test etme desteğine sahiptir: nspec.org
Mike

1
"Bill" i "Kullanıcı" olarak değiştirdiğinizde işleminiz tamamlanmıştır
Steven A. Lowe

Yanıtlar:


15

Evet, testlerinize test ettiğiniz örnek senaryoların adlarını vermek iyi bir fikirdir. Ünite test aracınızı sadece ünite testlerinden daha fazlası için kullanmak da iyi olabilir, birçok insan bunu başarılı bir şekilde yapar (ben de).

Ancak hayır, testlerinizin test sırasının önemli olduğu bir şekilde yazılması kesinlikle iyi bir fikir değildir. Örneğin, NUnit kullanıcının hangi testi yürütmek istediğini etkileşimli olarak seçmesine izin verir, böylece bu artık amaçlanan şekilde çalışmaz.

Burada her testin ana test kısmını ("assert" dahil) sisteminizi doğru başlangıç ​​durumuna getiren parçalardan ayırarak bunu kolayca önleyebilirsiniz. Yukarıdaki örneğinizi kullanarak: hesap oluşturmak, oturum açmak ve yorum yazmak için yöntemler yazın - herhangi bir iddiada bulunmaksızın. Ardından bu yöntemleri farklı testlerde tekrar kullanın. Ayrıca [Setup], sistemin uygun şekilde tanımlanmış bir başlangıç ​​durumunda olduğundan emin olmak için test fikstürlerinizin yöntemine bazı kodlar eklemeniz gerekecektir (örneğin, veritabanında şu ana kadar hiçbir hesap, şu ana kadar kimse bağlı değil).

EDIT: Tabii ki, bu testlerinizin "hikaye" doğasına aykırı gibi görünüyor, ancak yardımcı yöntemlerinize anlamlı isimler verirseniz, hikayelerinizi her testte bulursunuz.

Yani, şöyle görünmeli:

[TestFixture]
public class Authentication_Bill
{
    [Setup]
    public void Init()
    {  // bring the system in a predefined state, with noone logged in so far
    }

    [Test]
    public void Test_if_Bill_can_create_account()
    {
         CreateAccountForBill();
         // assert that the account was created properly 
    }

    [Test]
    public void Test_if_Bill_can_post_comment_after_login()
    { 
         // here is the "story" now
         CreateAccountForBill();
         LoginWithBillsAccount();
         AddCommentForBill();
        //  assert that the right things happened
    }

    private void CreateAccountForBill()
    {
        // ...
    }
    // ...
}

Daha ileri gideceğim ve fonksiyonel testleri çalıştırmak için bir xUnit aracı kullanmanın, takımı test türüyle karıştırmamanız ve bu testleri gerçek ünite testlerinden ayrı tuttuğunuz sürece iyi olduğunu söyleyebilirim. işlem sırasında birim testleri hızlı bir şekilde gerçekleştirmeye devam eder. Bunların birim testlerden çok daha yavaş olması muhtemeldir.
bdsl

4

Birim testleri ile bir hikaye anlatmakla ilgili bir sorun, birim testlerinin birbirinden tamamen bağımsız olarak düzenlenmesi ve yürütülmesi gerektiğini açıkça belirtmemesidir.

İyi bir birim testi diğer tüm bağımlı kodlardan tamamen izole edilmelidir, test edilebilen en küçük kod birimidir .

Bu, bir test başarısız olursa, kodun tam olarak ücretsiz olduğu yerde tam olarak teşhis koyarsanız, kodun çalışmasının onaylanmasının yanı sıra yarar sağlar. Bir test izole edilmemişse, neyin yanlış gittiğini tam olarak bulmak için neye bağlı olduğuna bakmak ve birim testin büyük bir faydasını kaçırmak zorundasınız. Uygulama sırasına sahip olmak da çok sayıda yanlış negatif oluşturabilir, eğer bir test başarısız olursa, test ettikleri kodun mükemmel çalışmasına rağmen aşağıdaki testlerin başarısız olması mümkündür.

Daha derinlemesine iyi bir makale, kirli melez testlerde klasiktir .

Sınıfları, yöntemleri ve sonuçları okunabilir hale getirmek için büyük Birim Sanat testi adlandırma kuralını kullanır

Test Sınıfı:

ClassUnderTestTests

Test yöntemleri:

MethodUnderTest_Condition_ExpectedResult

@Doc Brown örneğini kopyalamak için, her testten önce çalışan [Setup] kullanmak yerine, test etmek için yalıtılmış nesneler oluşturmak için yardımcı yöntemler yazarım.

[TestFixture]
public class AuthenticationTests
{
    private Authentication GetAuthenticationUnderTest()
    {
        // create an isolated Authentication object ready for test
    }

    [Test]
    public void CreateAccount_WithValidCredentials_CreatesAccount()
    {
         //Arrange
         Authentication codeUnderTest = GetAuthenticationUnderTest();
         //Act
         Account result = codeUnderTest.CreateAccount("some", "valid", "data");
         //Assert
         //some assert
    }

    [Test]
    public void CreateAccount_WithInvalidCredentials_ThrowsException()
    {
         //Arrange
         Authentication codeUnderTest = GetAuthenticationUnderTest();
         Exception result;
         //Act
         try
         {
             codeUnderTest.CreateAccount("some", "invalid", "data");
         }
         catch(Exception e)
         {
             result = e;
         }
         //Assert
         //some assert
    }
}

Bu nedenle başarısız testlerin, tam olarak hangi yöntemin başarısız olduğu, durum ve beklenen sonuç hakkında bazı anlatılar veren anlamlı bir adı vardır.

Her zaman birim testler yazdım, ama bir arkadaşım Gerkin ile çok başarılı oldu .


1
Bunun iyi bir gönderi olduğunu düşünsem de, bağlantılı makalenin "karma" test hakkında söylediklerine katılmıyorum. "Küçük" entegrasyon testlerine (ek olarak, saf birim testlerine alternatif olarak değil ) sahip olmak, IMHO'nun size tam olarak hangi yöntemin yanlış kod içerdiğini söyleyemese bile çok yardımcı olabilir. Bu testlerin bakımı yapılabiliyorsa, bu testlerin kodunun ne kadar temiz yazıldığına bağlıdır, kendi başlarına "kirli" değildir. Bence bu testlerin amacı çok net olabilir (OP örneğinde olduğu gibi).
Doc Brown

3

Açıkladığınız şey bana birim testinden çok Davranış Odaklı Tasarım (BDD) gibi geliyor . Gherkin DSL tabanlı bir .NET BDD teknolojisi olan SpecFlow'a bir göz atın .

Herhangi bir insanın kodlama hakkında hiçbir şey bilmeden okuyabileceği / yazabileceği güçlü şeyler. Test ekibimiz, entegrasyon test paketlerimiz için büyük başarılar elde ediyor.

Birim testleri kurallarına gelince, @ DocBrown'un yanıtı sağlam görünüyor.


Bilgi için, BDD tam olarak TDD'ye benzer, sadece değişen yazı stili. Örnek: TDD = assert(value === expected)BDD = value.should.equals(expected)+, "birim test bağımsızlığı" sorununu çözen katmanlardaki özellikleri açıklarsınız . Bu harika bir stil!
Offirmo
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.