it-swarm.dev

Tek bir ünite testinde birden fazla asistan bulundurmak uygun mudur?

bu harika gönderi için yapılan yorumda Roy Osherove, her bir tek bir testte her bir iddiayı çalıştırmak için tasarlanmış olan OAPT projesinden bahsetti.

Projenin ana sayfasında aşağıdakiler yazılmıştır:

Doğru birim testleri tam bir nedenden dolayı başarısız olmalıdır, bu yüzden birim test başına bir onaylayıcı kullanmalısınız.

Ve ayrıca, Roy şunları yazdı:

Yönergem genellikle test başına bir mantıksal KAVRAM test etmenizdir. aynı nesne üzerinde birden fazla ekleme yapabilirsiniz. bunlar genellikle test edilen aynı kavram olacaktır.

Bence, birden fazla iddiaya ihtiyaç duyulan bazı durumlar vardır (örneğin Guard Assertion ), ama genel olarak bundan kaçınmaya çalışıyorum. Senin görüşün nedir? Lütfen birden fazla ekin gerçekten gerekli olduğu gerçek bir dünya örneği sağlayın.

430
Restuta

Bunun mutlaka bir kötü bir şey olduğunu düşünmüyorum , ama sadece tek iddialar yapmaya çabalamamız gerektiğini düşünüyorum. testlerimizde. Bu, çok daha fazla test yazdığınız ve testlerimizin bir seferde yalnızca bir şeyi test edeceği anlamına gelir.

Bunu söyledikten sonra, testlerimin yarısının aslında sadece bir iddiası olduğunu söyleyebilirim. Ben sadece bir kod (test?) Kokusu testinizde yaklaşık beş veya daha fazla ekleri olduğunda olur.

Birden fazla öğeyi nasıl çözersiniz?

246
Jaco Pretorius

Testler yalnızca bir nedenle başarısız olmalıdır, ancak bu her zaman yalnızca bir Assert ifadesi olması gerektiği anlamına gelmez. IMHO " Düzenleme, Hareket Etme, İddia " modeline tutunmak daha önemlidir.

Anahtar, sadece bir eyleminiz olması ve daha sonra bu eylemin sonuçlarını varsayımlar kullanarak incelemenizdir. Ama "Düzenle, Harekete Geç, İddia, Testin sonu". Başka bir işlem gerçekleştirerek ve daha sonra daha fazla iddia yaparak teste devam etmek istiyorsanız, bunun yerine ayrı bir test yapın.

Aynı eylemi test etmenin parçalarını oluşturan birden fazla iddia ifadesi gördüğüm için mutluyum. Örneğin.

[Test]
public void ValueIsInRange()
{
  int value = GetValueToTest();

  Assert.That(value, Is.GreaterThan(10), "value is too small");
  Assert.That(value, Is.LessThan(100), "value is too large");
} 

veya

[Test]
public void ListContainsOneValue()
{
  var list = GetListOf(1);

  Assert.That(list, Is.Not.Null, "List is null");
  Assert.That(list.Count, Is.EqualTo(1), "Should have one item in list");
  Assert.That(list[0], Is.Not.Null, "Item is null");
} 

Siz olabilir bunları tek bir iddiada birleştirirsiniz, ancak bu gerekir veya gerekir. Bunları birleştirmekten bir gelişme yok.

örneğin. İlki olabilir be

Assert.IsTrue((10 < value) && (value < 100), "Value out of range"); 

Ancak bu daha iyi değildir - bunun dışındaki hata mesajı daha az belirgindir ve başka avantajı yoktur. İki veya üç (veya daha fazla) öğeyi bir büyük boole koşulunda birleştirmenin, okumayı zorlaştırdığını, değiştirmeyi zorlaştırdığını ve neden başarısız olduğunu çözmek için daha zor hale getirdiği diğer örnekleri düşünebileceğinizden eminim. Bunu neden sadece bir kural uğruna yapıyor?

[~ # ~] nb [~ # ~] : Burada yazdığım kod NUnit ile C #, ancak ilkeler diğer dillerle geçerli olacak ve çerçeveler. Sözdizimi de çok benzer olabilir.

314
Anthony

Birden fazla iddiada bulunmanın kötü bir şey olduğunu hiç düşünmemiştim.

Onu her zaman yaparım:

public void ToPredicateTest()
{
    ResultField rf = new ResultField(ResultFieldType.Measurement, "name", 100);
    Predicate<ResultField> p = (new ConditionBuilder()).LessThanConst(400)
                                                       .Or()
                                                       .OpenParenthesis()
                                                       .GreaterThanConst(500)
                                                       .And()
                                                       .LessThanConst(1000)
                                                       .And().Not()
                                                       .EqualsConst(666)
                                                       .CloseParenthesis()
                                                       .ToPredicate();
    Assert.IsTrue(p(ResultField.FillResult(rf, 399)));
    Assert.IsTrue(p(ResultField.FillResult(rf, 567)));
    Assert.IsFalse(p(ResultField.FillResult(rf, 400)));
    Assert.IsFalse(p(ResultField.FillResult(rf, 666)));
    Assert.IsFalse(p(ResultField.FillResult(rf, 1001)));

    Predicate<ResultField> p2 = (new ConditionBuilder()).EqualsConst(true).ToPredicate();

    Assert.IsTrue(p2(new ResultField(ResultFieldType.Confirmation, "Is True", true)));
    Assert.IsFalse(p2(new ResultField(ResultFieldType.Confirmation, "Is False", false)));
}

Burada karmaşık koşulların beklenen yükleme dönüştürülebildiğinden emin olmak için çoklu öneriler kullanıyorum.

Sadece bir birimi test ediyorum (ToPredicate yöntemi), ama testte aklıma gelen her şeyi anlatıyorum.

85
Matt Ellen

Yüksek seviyeli davranışı doğrulamak için birim testi kullanırken, kesinlikle tek bir teste birden fazla iddia koydum. İşte bazı acil durum bildirim kodu için kullandığım bir test. Testten önce çalışan kod, sistemi ana işlemcinin çalıştırılması durumunda bir alarmın gönderildiği duruma getirir.

@Test
public void testAlarmSent() {
    assertAllUnitsAvailable();
    assertNewAlarmMessages(0);

    pulseMainProcessor();

    assertAllUnitsAlerting();
    assertAllNotificationsSent();
    assertAllNotificationsUnclosed();
    assertNewAlarmMessages(1);
}

Kodun beklediğim gibi davrandığından emin olmam için sürecin her adımında bulunması gereken koşulları temsil ediyor. Tek bir iddia başarısız olursa, geri kalanların bile kaçmayacağı umurumda değil; sistemin durumu artık geçerli olmadığından, bu sonraki iddialar bana değerli bir şey söylemez. * assertAllUnitsAlerting() başarısız olursa, assertAllNotificationSent() 'ın success OR önceki hataya neyin neden olduğunu tespit edip düzelene kadar hata.

(* - Tamam, problemin hatalarını ayıklamada yararlı olabilirler. Ancak testin başarısız olduğu en önemli bilgiler zaten alındı.)

21
BlairHippo

Bir yöntemde birden fazla iddia kötü bir şey olmadığını düşünüyorum, başka bir nedeni aşağıdaki kodda açıklanmıştır:

class Service {
    Result process();
}

class Result {
    Inner inner;
}

class Inner {
    int number;
}

Testimde, service.process() öğesinin Inner sınıf örneklerinde doğru sayıyı döndürdüğünü test etmek istiyorum.

Test etmek yerine ...

@Test
public void test() {
    Result res = service.process();
    if ( res != null && res.getInner() != null ) Assert.assertEquals( ..., res.getInner() );
}

Yapıyorum

@Test
public void test() {
    Result res = service.process();
    Assert.notNull(res);
    Assert.notNull(res.getInner());
    Assert.assertEquals( ..., res.getInner() );
}
8
Betlista

Bir testin sadece bir nedenden dolayı başarısız olması gerektiği kuralında birden fazla iddia yazmanın geçerli olduğu birçok durum olduğunu düşünüyorum.

Örneğin, bir tarih dizesini ayrıştıran bir işlev düşünün:

function testParseValidDateYMD() {
    var date = Date.parse("2016-01-02");

    Assert.That(date.Year).Equals(2016);
    Assert.That(date.Month).Equals(1);
    Assert.That(date.Day).Equals(0);
}

Test başarısız olursa, bunun nedeni bir nedendir, ayrıştırma yanlıştır. Bu testin üç farklı nedenden dolayı başarısız olabileceğini iddia ederseniz, IMHO'nun "bir sebep" tanımınızda çok ince taneli olursunuz.

6
Pete

[Test] yönteminin içinde birden fazla iddiaya sahip olmanın iyi bir fikir olacağı hiçbir durum bilmiyorum. İnsanların birden fazla Bildiriye sahip olmaktan hoşlanmasının temel nedeni, test edilen her sınıf için bir tane [TestFixture] sınıfına sahip olmalarıdır. Bunun yerine, testlerinizi daha fazla [TestFixture] sınıfına ayırabilirsiniz. Bu, kodun yalnızca ilk iddianın başarısız olduğu kod yerine beklediğiniz şekilde tepki vermeyebileceği birden çok yol görmenizi sağlar. Bunu başarmanın yolu, her sınıfta çok sayıda [TestFixture] sınıfıyla test edilen en az bir dizininizin olmasıdır. Her [TestFixture] sınıfı, test edeceğiniz nesnenin belirli durumundan sonra adlandırılır. [SetUp] yöntemi, nesneyi sınıf adıyla açıklanan duruma getirir. Ardından, nesnenin geçerli durumu göz önüne alındığında, her birinin gerçek olmasını beklediğiniz farklı şeyleri öne süren birden fazla [Test] yönteminiz vardır. Her bir [Test] yöntemi, yalnızca kodun İngilizce okunması yerine, kavramdan sonra adlandırılabilmesi dışında, iddia ettiği şeyden sonra adlandırılır. Daha sonra her bir [Test] yöntemi uygulaması yalnızca bir şey iddia ettiği tek bir kod satırına ihtiyaç duyar. Bu yaklaşımın bir başka avantajı da, ne test ettiğinizi ve sadece sınıf ve yöntem adlarına bakarak ne beklediğinizi netleştirdiği için testleri çok okunabilir hale getirmesidir. Bu, test etmek istediğiniz tüm küçük Edge vakalarını fark etmeye başladığınızda ve hataları bulduğunuzda daha iyi ölçeklendirilir.

Bu genellikle [SetUp] yöntemi içindeki son kod satırının [TestFixture] özel örnek değişkeninde bir özellik değeri veya dönüş değeri depolaması gerektiği anlamına gelir. Ardından, bu örnek değişkeni hakkında farklı [Test] yöntemlerinden farklı şeyler iddia edebilirsiniz. Ayrıca, test edilen nesnenin farklı özelliklerinin şimdi istenen durumda olduğu için ayarlandığı hakkında da iddialarda bulunabilirsiniz.

Bazen nesneyi istenen duruma getirmeden önce karışıklık yapmadığınızdan emin olmak için test edilen nesneyi istenen duruma getirirken yol boyunca iddialarda bulunmanız gerekir. Bu durumda, bu ek iddialar [SetUp] yönteminin içinde görünmelidir. [SetUp] yönteminde bir şeyler ters giderse, nesne test etmek istediğiniz duruma gelmeden önce testte bir sorun olduğu açıktır.

Karşılaşabileceğiniz diğer bir sorun da, atılmasını beklediğiniz bir Özel Durumu test ediyor olabilirsiniz. Bu sizi yukarıdaki modeli izlememeye yönlendirebilir. Bununla birlikte, yine de [SetUp] yöntemindeki istisnayı yakalayıp bunu bir örnek değişkenine depolayarak elde edilebilir. Bu, her biri kendi [Test] yönteminde istisna hakkında farklı şeyler iddia etmenizi sağlayacaktır. Daha sonra, atılan istisnanın istenmeyen yan etkilerinin olmadığından emin olmak için test edilen nesne hakkında başka şeyler de iddia edebilirsiniz.

Örnek (bu birden çok dosyaya bölünür):

namespace Tests.AcctTests
{
    [TestFixture]
    public class no_events
    {
        private Acct _acct;

        [SetUp]
        public void SetUp() {
            _acct = new Acct();
        }

        [Test]
        public void balance_0() {
            Assert.That(_acct.Balance, Is.EqualTo(0m));
        }
    }

    [TestFixture]
    public class try_withdraw_0
    {
        private Acct _acct;
        private List<string> _problems;

        [SetUp]
        public void SetUp() {
            _acct = new Acct();
            Assert.That(_acct.Balance, Is.EqualTo(0));
            _problems = _acct.Withdraw(0m);
        }

        [Test]
        public void has_problem() {
            Assert.That(_problems, Is.EquivalentTo(new string[] { "Withdraw amount must be greater than zero." }));
        }

        [Test]
        public void balance_not_changed() {
            Assert.That(_acct.Balance, Is.EqualTo(0m));
        }
    }

    [TestFixture]
    public class try_withdraw_negative
    {
        private Acct _acct;
        private List<string> _problems;

        [SetUp]
        public void SetUp() {
            _acct = new Acct();
            Assert.That(_acct.Balance, Is.EqualTo(0));
            _problems = _acct.Withdraw(-0.01m);
        }

        [Test]
        public void has_problem() {
            Assert.That(_problems, Is.EquivalentTo(new string[] { "Withdraw amount must be greater than zero." }));
        }

        [Test]
        public void balance_not_changed() {
            Assert.That(_acct.Balance, Is.EqualTo(0m));
        }
    }
}
3
still_dreaming_1

Tek bir test fonksiyonunda birden fazla ekiniz varsa, bunların gerçekleştirdiğiniz testle doğrudan alakalı olmasını bekliyorum. Örneğin,

@Test
test_Is_Date_segments_correct {

   // It is okay if you have multiple asserts checking dd, mm, yyyy, hh, mm, ss, etc. 
   // But you would not have any assert statement checking if it is string or number,
   // that is a different test and may be with multiple or single assert statement.
}

Çok fazla teste sahip olmak (muhtemelen aşırıya kaçtığını hissettiğinde bile) kötü bir şey değildir. Hayati ve en önemli testlere sahip olmanın daha önemli olduğunu iddia edebilirsiniz. Bu nedenle, iddia ettiğinizde, iddia ifadelerinizin birden fazla iddia hakkında endişelenmek yerine doğru yerleştirildiğinden emin olun. Birden fazlasına ihtiyacınız varsa, birden fazlasını kullanın.

2
hagubear

Aynı testte birden fazla iddia olması yalnızca test başarısız olduğunda bir sorundur. Ardından, hangi hatanın başarısız olduğunu bulmak için testte hata ayıklamanız veya istisnayı analiz etmeniz gerekebilir. Her testte bir iddia ile neyin yanlış olduğunu belirlemek genellikle daha kolaydır.

Her zaman aynı iddiada birden çok koşul olarak yeniden yazabileceğiniz için çoklu iddiaların gerçekten gerekli olduğu bir senaryo düşünemiyorum. Bununla birlikte, örneğin, sonraki girişlerin hatalı girdi nedeniyle çökme riskini göze almak yerine, adımlar arasındaki ara verileri doğrulamak için birkaç adımınız varsa tercih edilebilir.

2
Guffa

Testiniz başarısız olursa, aşağıdaki iddiaların da kesilip kesilmeyeceğini bilemezsiniz. Çoğu zaman, sorunun kaynağını bulmak için değerli bilgileri kaçırdığınız anlamına gelir. Benim çözümüm bir iddia kullanmak ama birkaç değerle:

String actual = "val1="+val1+"\nval2="+val2;
assertEquals(
    "val1=5\n" +
    "val2=hello"
    , actual
);

Bu, tüm başarısız iddiaları bir kerede görmemi sağlar. Çoğu IDE'ler bir karşılaştırma iletişim kutusunda yan yana dize farklılıkları görüntüler çünkü birkaç satır kullanın.

2
Aaron Digulla

Birim testinin amacı, neyin başarısız olduğu hakkında mümkün olduğunca fazla bilgi vermek ve aynı zamanda önce en temel sorunları doğru bir şekilde belirlemenize yardımcı olmaktır. Mantıksal olarak, bir başka iddianın başarısız olduğu veya başka bir deyişle test arasında bir bağımlılık ilişkisi olduğu takdirde bir iddianın başarısız olacağını bildiğinizde, bunları tek bir test içinde birden fazla iddia olarak yuvarlamak mantıklıdır. Bu, test sonuçlarını, tek bir testte ilk iddiayı kurtardığımızda ortadan kaldırılabilecek bariz arızalarla alt üst etmemenin yararıdır. Bu ilişkinin mevcut olmaması durumunda, tercih doğal olarak bu iddiaları bireysel testlere ayırmak olacaktır, çünkü aksi takdirde bu başarısızlıkları bulmak, tüm sorunları çözmek için birden fazla test çalıştırması yinelemesi gerektirecektir.

Daha sonra birimleri/sınıfları aşırı karmaşık testlerin yazılması gerektiği şekilde tasarlarsanız, test sırasında daha az yük oluşturur ve muhtemelen daha iyi bir tasarım sağlar.

1
jpierson

Evet, birden fazla iddiaya sahip olmanız uygundur sürece başarısız bir test size hatayı teşhis edebilmek için yeterli bilgi verir. Bu, ne test ettiğinize ve hata modlarının ne olduğuna bağlı olacaktır.

Doğru birim testleri tam bir nedenden dolayı başarısız olmalıdır, bu yüzden birim test başına bir onaylayıcı kullanmalısınız.

Asla yardımcı olmak için bu tür formülasyonlar bulamadım (bir sınıfın değiştirmek için bir nedeni olması gerektiği gibi sadece yararsız bir atasözü bir örnek). İki dizenin eşit olduğu iddiasını düşünün, bu semantik olarak iki dizenin uzunluğunun aynı olduğunu ve karşılık gelen dizindeki her karakterin eşit olduğunu iddia etmekle eşdeğerdir.

Genelleştirebilir ve herhangi bir çoklu iddia sisteminin tek bir iddia olarak yeniden yazılabileceğini ve herhangi bir tek iddia bir dizi daha küçük iddiaya ayrılabileceğini söyleyebiliriz.

Bu nedenle, kodun netliğine ve test sonuçlarının netliğine odaklanın ve bunun tersi yerine kullandığınız iddia sayısını yönlendirmesine izin verin.

1
CurtainDog

Bu soru, spagetti ve lazanya kod sorunları arasındaki klasik dengeleme sorunu ile ilgilidir.

Birden fazla ileriye sahip olmak, testin ne hakkında olduğu hakkında bir fikriniz olmadığı spagetti problemine kolayca girebilir, ancak test başına tek bir iddiaya sahip olmak, testinizin büyük bir lazanyada birden fazla teste sahip olmasını, hangi testin imkansız olduğunu bulmasını sağlayabilir. .

Bazı istisnalar vardır, ancak bu durumda sarkaçın ortada tutulması cevaptır.

0
gsf

Cevap çok basit - aynı nesnenin, hatta iki farklı nesnenin birden fazla niteliğini değiştiren bir işlevi test ederseniz ve işlevin doğruluğu tüm bu değişikliklerin sonuçlarına bağlıdır, o zaman iddia etmek istersiniz bu değişikliklerin her birinin düzgün bir şekilde gerçekleştirildiğini!

Mantıksal bir kavram fikrini anladım, ancak tersine sonuç hiçbir fonksiyonun birden fazla nesneyi değiştirmemesi gerektiğini söyler. Ama bu benim durumumda tüm durumlarda uygulanması imkansız.

Bir banka işleminin mantıksal kavramını ele alalım - çoğu durumda bir banka hesabından bir miktar çekmek, bu tutarı başka bir hesaba eklemeyi GEREKİR. Bu iki şeyi ASLA ayırmak istemezsiniz, atomik bir birim oluştururlar. İki işlev (para çekme/addMoney) yapmak ve böylece iki farklı birim testi yazmak isteyebilirsiniz - buna ek olarak. Ancak bu iki işlem bir işlem içinde gerçekleşmelidir ve işlemin çalıştığından da emin olmak istersiniz. Bu durumda, tek tek adımların başarılı olduğundan emin olmak yeterli değildir. Testinizde her iki banka hesabını da kontrol etmelisiniz.

Bir birim testinde, ilk etapta değil, bir entegrasyon veya kabul testinde test edemeyeceğiniz daha karmaşık örnekler olabilir. Ama bu sınırlar akıcı, IMHO! Karar vermek o kadar kolay değil, şartlar ve belki de kişisel tercih meselesi. Birinden para çekmek ve başka bir hesaba eklemek hala çok basit bir işlevdir ve kesinlikle birim test adayıdır.

0
cslotty