Test Etme vs Kendini Tekrar Etme (KURU)


11

Testler yazarak kendinizi tekrar etmek neden bu kadar teşvik edilir?

Testler temel olarak kodla aynı şeyi ifade eder ve bu nedenle kodun bir kopyasıdır (kavram olarak, uygulamada değil). DRY'nin nihai hedefi tüm test kodlarının kaldırılmasını içermez mi?

Yanıtlar:


25

Bunun herhangi bir şekilde yanlış düşünebileceğine inanıyorum.

Üretim kodunu test eden test kodu hiç de benzer değil. Python'da göstereceğim:

def multiply(a, b):
    """Multiply ``a`` by ``b``"""
    return a*b

O zaman basit bir test şöyle olur:

def test_multiply():
    assert multiply(4, 5) == 20

Her iki işlev de benzer bir tanıma sahiptir, ancak her ikisi de çok farklı şeyler yapar. Burada yinelenen kod yok. ;-)

İnsanların esasen test fonksiyonu başına bir iddiası olan yinelenen testler yazmaları da oluşur. Bu delilik ve insanların bunu yaptığını gördüm. Bu ise kötü uygulama.

def test_multiply_1_and_3():
    """Assert that a multiplication of 1 and 3 is 3."""
    assert multiply(1, 3) == 3

def test_multiply_1_and_7():
    """Assert that a multiplication of 1 and 7 is 7."""
    assert multiply(1, 7) == 7

def test_multiply_3_and_4():
    """Assert that a multiplication of 3 and 4 is 12."""
    assert multiply(3, 4) == 12

Bunu 1000'den fazla etkili kod satırı için yaptığınızı düşünün. Bunun yerine 'özellik' temelinde test yaparsınız:

def test_multiply_positive():
    """Assert that positive numbers can be multiplied."""
    assert multiply(1, 3) == 3
    assert multiply(1, 7) == 7
    assert multiply(3, 4) == 12

def test_multiply_negative():
    """Assert that negative numbers can be multiplied."""
    assert multiply(1, -3) == -3
    assert multiply(-1, -7) == 7
    assert multiply(-3, 4) == -12

Şimdi özellikler eklendiğinde / kaldırıldığında sadece bir test fonksiyonu eklemeyi / kaldırmayı düşünmeliyim.

forDöngüler uygulamadığımı fark etmiş olabilirsiniz . Çünkü bazı şeyleri tekrarlamak iyidir. Döngüler uygularsam kod çok daha kısa olurdu. Ancak bir iddia başarısız olduğunda, çıktı belirsiz bir mesaj göstererek gizlenebilir. Bu durumda, testleriniz daha az yararlı olacaktır ve işlerin nerede yanlış gittiğini incelemek için bir hata ayıklayıcıya ihtiyacınız olacaktır .


8
Test başına bir iddia teknik olarak önerilmektedir çünkü bu, birden fazla sorunun sadece tek bir hata olarak gösterilmeyeceği anlamına gelir. Bununla birlikte, pratikte, iddiaların dikkatli bir şekilde toplanması tekrarlanan kod miktarını azaltır ve neredeyse hiçbir zaman test kılavuzu başına bir iddiaya bağlı kalmam.
Rob Kilisesi

@ pembe-elmas-kare Ben bir iddia başarısız olduktan sonra NUnit test durdurmak olmadığını görüyorum (ki bu garip olduğunu düşünüyorum). Bu özel durumda, test başına bir iddiaya sahip olmak gerçekten daha iyidir. Bir birim test çerçevesi başarısız bir iddiadan sonra testi durdurursa, birden fazla iddia daha iyidir.
siebz0r

3
NUnit tüm test paketini durdurmaz, ancak bunu önlemek için adımlar atmadıkça bir test durur (attığı istisnayı yakalayabilirsiniz, bu da bazen yararlıdır). Yaptıkları nokta, birden fazla iddia içeren testler yazarsanız, sorunu düzeltmek için ihtiyacınız olan tüm bilgileri alamayacağınızdır. Örneğin üzerinde çalışmak için, bu çarpma işlevinin 3 sayısını beğenmediğini düşünün. Bu durumda assert multiply(1,3)başarısız olur, ancak başarısız test raporunu da alamazsınız assert multiply(3,4).
Rob Kilisesi

Ben sadece bunu yükselteceğimi düşündüm çünkü test başına tek bir iddia, .net dünyasında okuduğumdan, "iyi uygulama" ve çoklu iddialar "pragmatik kullanım" dır. Örneğin iki açıklama yaptığı Python belgelerinde biraz farklı görünüyor def test_shuffle.
Rob Kilisesi

Kabul ediyorum ve katılmıyorum: D Burada açıkça tekrar var: assert multiply(*, *) == *böylece bir assert_multiplyişlevi tanımlayabilirsiniz . Mevcut senaryoda satır sayısı ve okunabilirliği önemli değildir, ancak daha uzun testlerle karmaşık iddiaları, fikstürleri, fikstür üreten kodu vb. Tekrar kullanabilirsiniz. Bunun en iyi uygulama olup olmadığını bilmiyorum, ama genellikle bu.
inf3rno

10

Testler temel olarak kodla aynı şeyi ifade ediyor gibi görünüyor ve bu nedenle de

Hayır, bu doğru değil.

Testlerin uygulamanızdan farklı bir amacı vardır:

  • Testler uygulamanızın çalıştığından emin olur.
  • Bir doküman olarak hizmet ederler: Testlere bakarak, kodunuzun yerine getirmesi gereken sözleşmeleri, yani hangi girdinin hangi çıktıyı, özel durumlar vb.
  • Ayrıca, testleriniz yeni özellikler ekledikçe mevcut işlevlerinizin bozulmadığını garanti eder.

4

Hayır. KURU belirli bir görevi yapmak için sadece bir kez kod yazmakla ilgilidir, testler görevin doğru bir şekilde yapıldığını doğrular. Aynı kodu kullanmanın işe yaramayacağı bir oylama algoritmasına benziyor.


2

DRY'nin nihai hedefi tüm test kodlarının kaldırılmasını içermez mi?

Hayır, DRY'nin nihai hedefi aslında tüm üretim kodlarının kaldırılması anlamına gelir .

Testlerimiz, sistemin yapmasını istediğimiz şeyin mükemmel özellikleri olabilirse, ilgili üretim kodunu (veya ikili dosyaları) otomatik olarak oluşturmanız ve üretim kodu tabanını etkili bir şekilde kaldırmamız gerekir.

Model güdümlü mimari gibi yaklaşımların başardığını iddia eden şey budur - her şeyin hesaplamayla türetildiği, insan tarafından tasarlanmış tek bir gerçek kaynağı.

Ters (tüm testlerden kurtulmak) arzu olduğunu sanmıyorum çünkü:

  • Uygulama ve belirtim arasındaki empedans uyumsuzluğunu çözmelisiniz. Üretim kodu niyeti bir dereceye kadar iletebilir, ancak iyi ifade edilmiş testler hakkında akıl yürütmek asla bu kadar kolay olmayacaktır. Biz insanlar neden bir şeyler inşa ettiğimize dair daha yüksek bir görüşe ihtiyacımız var . DRY nedeniyle test yapmasanız bile, spesifikasyonların muhtemelen belgelere yazılması gerekecektir, bu da bana sorarsanız empedans uyuşmazlığı ve kod senkronizasyonu açısından kesinlikle daha tehlikeli bir canavar.
  • Üretim kodu tartışmalı olarak doğru yürütülebilir özelliklerden kolayca elde edilebilir (yeterli zaman varsayarak), bir test paketinin bir programın nihai kodundan yeniden oluşturulması çok daha zordur. Spesifikasyonlar sadece koda bakarak net bir şekilde görünmüyor, çünkü çalışma zamanında kod birimleri arasındaki etkileşimlerin gerçekleştirilmesi zor. Bu yüzden test edilmemiş eski uygulamalarla uğraşmakta zorlanıyoruz. Başka bir deyişle: uygulamanızın birkaç aydan daha uzun süre hayatta kalmasını istiyorsanız, üretim kod tabanınızı barındıran sabit sürücüyü test paketinizin bulunduğu yerden kaybetmekten daha iyi olur.
  • Üretim koduna yanlışlıkla bir hata eklemek test koduna göre çok daha kolaydır. Ve üretim kodu kendi kendini doğrulamadığı için (buna Sözleşme ile Tasarım veya daha zengin tip sistemler ile yaklaşılabilir), yine de test etmek ve bir gerileme meydana gelirse bizi uyarmak için bazı harici programlara ihtiyacımız var.

1

Çünkü bazen kendini tekrarlamak iyidir. Bu ilkelerin hiçbiri, her durumda soru veya bağlam olmaksızın alınamaz. DRY'nin oldukça açık bir ihlali olan ama kesinlikle faydalı olan bir algoritmanın saf (ve yavaş) versiyonuna karşı testler yazdım.


1

Birim testi istemeden yapılan değişiklikleri zorlaştırmakla ilgili olduğundan, bazen de kasıtlı değişiklikleri zorlaştırabilir. Bu gerçek gerçekten KURU prensibi ile ilgilidir.

Örneğin, MyFunctionüretim kodunda yalnızca bir yerde çağrılan bir işleviniz varsa ve bunun için 20 birim sınama yazarsanız, kodunuzda bu işlevin çağrıldığı 21 yere kolayca sahip olabilirsiniz. Şimdi, imzasını MyFunctionveya anlambilimini veya her ikisini de değiştirmek zorunda kaldığınızda (bazı gereksinimler değiştiğinden), sadece bir tane yerine 21 yeriniz olacak. Ve neden gerçekten KURU prensibinin ihlalidir: aynı işlevi çağrıyı MyFunction21 kez tekrarladınız (en azından) .

Böyle bir durum için doğru yaklaşım, DRY prensibini test kodunuza da uygulamaktır: 20 ünite testi yazarken, ünite testlerinizdeki çağrıları MyFunction, sadece birkaç yardımcı fonksiyonda (ideal olarak sadece bir tane) kapsüle edin . 20 birim test. İdeal olarak, kod çağrınızda sadece iki yer elde edersiniz MyFunction: biri üretim kodunuzdan ve diğeri birim testleri. MyFunctionDaha sonra imzasını değiştirmeniz gerektiğinde , testlerinizde değiştirmek için sadece birkaç yeriniz olacak.

"Birkaç yer" hala "tek bir yerden" daha fazladır ( hiç birim testi olmadan elde ettiğiniz şey ), ancak birim testlerine sahip olmanın avantajları, değiştirmek için daha az koda sahip olmanın avantajından ağır basmalıdır (aksi takdirde birim testi tamamen yapıyorsunuz) yanlış).


0

Yazılım oluşturmada karşılaşılan en büyük zorluklardan biri gereksinimleri yakalamaktır; yani "bu yazılım ne yapmalı?" Yazılım, sistemin ne yapması gerektiğini doğru bir şekilde tanımlamak için kesin gereksinimlere ihtiyaç duyar, ancak yazılım sistemleri ve projeleri için ihtiyaçları tanımlayanlar genellikle yazılım veya resmi (matematik) arka planı olmayan kişileri içerir. Gereksinimde titizlik olmaması, yazılımı gereksinimlere doğrulamanın bir yolunu bulmak için yazılım geliştirmeyi zorladı.

Geliştirme ekibi kendilerini bir proje için konuşma diline özgü tanımlamayı daha titiz gereksinimlere çevirirken buldu. Test disiplini, yazılım geliştirme, müşterinin istediklerini söyledikleri ile yazılımın istediklerini anladıkları arasındaki boşluğu kapatmak için bir kontrol noktası olarak birleşti. Hem yazılım geliştiriciler hem de kalite / test ekibi, (gayri resmi) spesifikasyonun anlaşılmasını oluşturur ve her biri (bağımsız olarak), anlayışlarının uygun olmasını sağlamak için yazılım veya testler yazar. (Kesin olmayan) gereksinimleri anlamak için başka bir kişi eklemek, gereksinimlerin doğruluğunu daha da geliştirmek için sorular ve farklı bir bakış açısı ekledi.

Her zaman kabul testi olduğundan, otomatik ve birim testleri yazmak için test rolünü genişletmek doğaldı. Sorun, programcıların test yapması için işe alınması anlamına geliyordu ve böylece perspektifi kalite güvencesinden test yapan programcılara kadar daralttınız.

Tüm bunlar, testleriniz gerçek programlardan biraz farklıysa muhtemelen test yanlış yapıyorsunuz dedi. Msdy'nin önerisi, testlerde nelere daha fazla, nasıl daha azına odaklanmak olacaktır.

İroni, konuşma dilindeki açıklamanın gerekliliklerinin resmi bir şartnamesini yakalamak yerine, endüstrinin testi otomatikleştirmek için kod olarak nokta testleri uygulamayı seçmiş olmasıdır. Cevaplamak için hangi yazılımın geliştirilebileceği biçimsel gereksinimler üretmek yerine, yaklaşım, biçimsel mantığı kullanarak bina yazılımına yaklaşmak yerine birkaç noktayı test etmek olmuştur. Bu bir uzlaşmadır, ancak oldukça etkili ve nispeten başarılı olmuştur.


0

Test kodunuzun uygulama kodunuza çok benzediğini düşünüyorsanız, bu alaycı bir çerçeveyi aşırı kullandığınızın bir göstergesi olabilir. Çok düşük seviyede sahte tabanlı testler, test kurulumunun test edilen yönteme çok benzemesi ile sonuçlanabilir. Uygulamanızı değiştirirseniz kırılma olasılığı daha düşük olan daha yüksek seviyeli testler yazmaya çalışın (bunun zor olabileceğini biliyorum, ancak yönetebiliyorsanız sonuç olarak daha kullanışlı bir test paketiniz olacaktır).


0

Birim testleri, daha önce belirtildiği gibi test edilen kodun bir kopyasını içermemelidir.

Ancak, birim testleri genellikle "üretim" kodu kadar DRY değildir, çünkü kurulum testleri arasında benzer (ama aynı değil) eğilimindedir ... özellikle alaycı olduğunuz önemli sayıda bağımlılık varsa / numara.
Elbette bu tür şeyleri ortak bir kurulum yöntemine (veya kurulum yöntemleri setine) yeniden yansıtmak mümkündür ... ancak bu kurulum yöntemlerinin uzun parametre listelerine sahip olma ve oldukça kırılgan olma eğiliminde olduğunu gördüm.

Yani pragmatik olun. Kurulum kodunu sürdürülebilirlikten ödün vermeden birleştirebilirseniz, kesinlikle yapın. Ancak alternatif, karmaşık ve kırılgan bir kurulum yöntemleri kümesiyse, test yöntemlerinizde biraz tekrarlama tamamdır.

Yerel bir TDD / BDD evangelisti bunu şu şekilde ortaya koyar:
"Üretim kodunuz KURU olmalıdır. Ancak testlerinizin 'nemli olması sorun değil."


0

Testler temel olarak kodla aynı şeyi ifade eder ve bu nedenle kodun bir kopyasıdır (kavram olarak, uygulamada değil).

Bu doğru değildir, testler kullanım durumlarını açıklarken, kod kullanım durumlarını geçen bir algoritmayı açıklar, bu yüzden daha geneldir. TDD ile kullanım senaryoları yazmaya başlıyorsunuz (muhtemelen kullanıcı hikayesine dayanarak) ve bundan sonra bu kullanım senaryolarını geçmek için gerekli kodu uygularsınız. Böylece küçük bir test, küçük bir kod parçası yazıyorsunuz ve bundan sonra tekrarlardan kurtulmak için gerekirse yeniden gözden geçiriyorsunuz. İşte böyle çalışır.

Testlerle tekrarlar da olabilir. Örneğin, armatürleri, fikstür üreten kodu, karmaşık iddiaları vb.Kullanabilirsiniz ... Testlerdeki hataları önlemek için genellikle bunu yaparım, ancak genellikle önce bir testin gerçekten başarısız olup olmadığını test etmeyi unuturum ve günü gerçekten mahvedebilir , koddaki hatayı yarım saat aradığınızda ve test yanlış olduğunda ... xD

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.