Yeniden tasarlarken testlerinizi verimli bir şekilde nasıl sürdürüyorsunuz?


15

İyi test edilmiş bir kod tabanının birçok faydası vardır, ancak sistemin belirli yönlerini test etmek, bazı değişiklik türlerine dayanıklı bir kod tabanıyla sonuçlanır.

Bir örnek, belirli bir çıktıyı test etmektir - örneğin, metin veya HTML. Bazı girdi parametreleri için çıktı olarak belirli bir metin bloğunu beklemek veya bir bloktaki belirli bölümleri aramak için testler genellikle (naif?) Yazılır.

Kodun davranışını değiştirmek, yeni gereksinimleri karşılamak ya da kullanılabilirlik testi arayüzde değişikliğe yol açtığı için, testlerin de değiştirilmesi gerekir - belki de kodun özel olarak birim testleri olmayan testler bile.

  • Bu testleri bulma ve yeniden yazma çalışmalarını nasıl yönetiyorsunuz? Ya "hepsini çalıştırıp çerçevenin bunları çözmesine izin veremezseniz" ne olur?

  • Alışılmadık derecede kırılgan testlere başka hangi tür test altı kodu neden olur?


Bu programcılar.stackexchange.com/ questions/5898/… 'dan önemli ölçüde farklı mı?
AShelly

4
Bu soru yanlışlıkla yeniden düzenleme hakkında sorulmuştur - birim testleri yeniden düzenleme altında değişmez olmalıdır.
Alex Feinman

Yanıtlar:


10

TDD milletinin bu cevaptan nefret edeceğini biliyorum, ancak bunun büyük bir kısmı bir şeyi nerede test edeceğinizi dikkatlice seçmektir.

Alt katmanlardaki birim testleri ile çok çıldırırsam, birim testlerini değiştirmeden anlamlı bir değişiklik yapılamaz. Arayüz asla açığa çıkmazsa ve uygulamanın dışında yeniden kullanılmak üzere tasarlanmamışsa, bu, aksi takdirde hızlı bir değişiklik olmuş olabilecek şeylere gereksizdir.

Tersine, değiştirmeye çalıştığınız şey maruz kalırsa veya yeniden kullanılırsa, değiştirmeniz gereken testlerin her biri başka bir yerde kırdığınız bir şeyin kanıtıdır.

Bazı projelerde bu, testlerinizi ünite testlerinden ziyade kabul seviyesinden tasarlamaya kadar olabilir. ve daha az birim testi ve daha fazla entegrasyon stili testi yapılması.

Bu, kabul edilebilirlik kriterlerini karşılayana kadar tek bir özelliği ve kodu tanımlayamayacağınız anlamına gelmez. Bu, bazı durumlarda kabul kriterlerini birim testlerle ölçmemeniz anlamına gelir.


Bence "uygulamanın dışında" değil, "modülün dışında" yazmak istediniz.
SamB

SamB, duruma göre değişir. Arayüz bir uygulama ile birkaç yere dahili, ancak halka açık değilse, arayüzün geçici olabileceğini düşünürsem daha yüksek seviyede test etmeyi düşünürüm.
Bill

Bu yaklaşımın TDD ile çok uyumlu olduğunu gördüm. Uygulamanın üst katmanlarında son kullanıcıya daha yakın başlamayı seviyorum, böylece üst katmanların alt katmanları nasıl kullanması gerektiğini bilerek alt katmanları tasarlayabilirim. Esasen yukarıdan aşağıya inşa etmek, bir katman ve bir diğeri arasındaki arayüzü daha doğru bir şekilde tasarlamanızı sağlar.
Greg Burghardt

4

Tüm TCP aktarımını yeniden yazarak SIP yığınımın büyük bir revizyonunu tamamladım. (Bu, çoğu refactoring'e göre oldukça büyük bir ölçekte neredeyse bir refaktördü.)

Kısacası TIdSipTcpTransport, TIdSipTransport'un alt sınıfı var. Tüm TIdSipTransports ortak bir test paketini paylaşır. TIdSipTcpTransport'un içinde bir dizi sınıf vardı - bağlantı / başlatma mesajı çiftlerini, iş parçacıklı TCP istemcilerini, iş parçacıklı bir TCP sunucusunu vb. İçeren bir harita.

İşte yaptım:

  • Değiştireceğim sınıfları sildim.
  • Bu sınıflar için test paketleri silindi.
  • Sol TIdSipTcpTransport için test paketi özgül (ve test paketi yaygın tüm TIdSipTransports hala orada).
  • Hepsinin başarısız olduğundan emin olmak için TIdSipTransport / TIdSipTcpTransport testlerini çalıştırın.
  • Bir TIdSipTransport / TIdSipTcpTransport testi hariç tümü yorumladı.
  • Eğer bir sınıf eklemek gerekiyorsa, tek uncommented test geçti yeterli işlevselliği oluşturmak için yazma testleri eklemek istiyorum.
  • Köpürtün, durulayın, tekrarlayın.

Böylece yorum yapmam gereken testler (*) şeklinde hâlâ ne yapmam gerektiğini biliyordum ve yazdığım yeni testler sayesinde yeni kodun beklendiği gibi çalıştığını biliyordum.

(*) Gerçekten, yorum yapmak zorunda değilsiniz. Sadece onları yönetme; 100 başarısız test çok cesaret verici değil. Ayrıca, benim özel kurulumda daha az test derlemek daha hızlı bir test-yazma-refactor döngü anlamına gelir.


Bunu birkaç ay önce de yaptım ve bu benim için oldukça iyi çalıştı. Bununla birlikte, etki alanı modeli modülümüzün (bu da projedeki diğer tüm modüllerin yeniden tasarımını tetikledi) yeniden tasarımında bir meslektaşımla eşleştirirken bu yöntemi kesinlikle uygulayamadım.
Marco Ciambrone

3

Testler kırılgan olduğunda, genellikle yanlış olanı test ettiğim için bulurum. Örneğin, HTML çıktısını ele alalım. Gerçek HTML çıktısını kontrol ederseniz testiniz hassas olacaktır. Ancak asıl çıktı ile ilgilenmiyorsunuz, olması gereken bilgiyi iletip iletmediğiyle ilgileniyorsunuz. Ne yazık ki, bunu yapmak kullanıcının beyninin içeriği hakkında iddialarda bulunmayı gerektirir ve bu nedenle otomatik olarak yapılamaz.

Yapabilirsin:

  • Gerçekte çalıştığından emin olmak için HTML'yi bir duman testi olarak oluşturun
  • Bir şablon sistemi kullanın, böylece şablonun tam olarak kendisini test etmeden şablon işlemcisini ve şablona gönderilen verileri test edebilirsiniz.

Aynı tür şeyler SQL'de de olur. Gerçek SQL'i iddia ederseniz, sınıflarınız başınızı belaya sokmaya çalışır. Sonuçları gerçekten iddia etmek istiyorsunuz. Bu nedenle, birim testlerim sırasında bir SQLITE bellek veritabanı kullanıyorum ve SQL'imin gerçekte ne yapması gerektiğinden emin olmak için.


Yapısal HTML'nin kullanılmasına da yardımcı olabilir.
SamB

@SamB kesinlikle yardımcı olur, ancak sorunun tamamen çözüleceğini sanmıyorum
Winston Ewert

tabii ki hayır, hiçbir şey yapamaz :-)
SamB

-1

İlk olarak, YENİ API ​​davranışınızın olmasını istediğiniz şeyi yapan bir YENİ API ​​oluşturun. Bu yeni API bir OLDER API ile aynı ada sahipse, _NEW adını yeni API adına eklerim.

int DoSomethingInterestingAPI ();

dönüşür:

int DoSomethingInterestingAPI_NEW (int, daha fazla_arguments alır); int DoSomethingInterestingAPI_OLD (); int DoSomethingInterestingAPI () {DoSomethingInterestingAPI_NEW (whatever_default_mimics_the_old_API); Tamam - bu aşamada - tüm regresyon testleriniz eşik kartı - DoSomethingInterestingAPI () adını kullanarak.

SONRAKİ, kodunuzu gözden geçirin ve DoSomethingInterestingAPI () öğesine yapılan tüm çağrıları DoSomethingInterestingAPI_NEW () değişkenine uygun olarak değiştirin. Bu, yeni API'yı kullanmak için regresyon testlerinizin hangi bölümlerinin değiştirilmesi gerektiğini güncellemeyi / yeniden yazmayı içerir.

SONRAKİ, DoSomethingInterestingAPI_OLD () öğesini [[kullanımdan kaldırıldı ()]] olarak işaretleyin. Kullanımdan kaldırılmış API'yı istediğiniz kadar (ona bağlı olabilecek tüm kodu güvenli bir şekilde güncelleyene kadar) saklayın.

Bu yaklaşımla, regresyon testlerinizdeki herhangi bir başarısızlık, o regresyon testindeki hatalardır veya kodunuzdaki hataları tam olarak istediğiniz gibi tanımlar. API'nın _NEW ve _OLD sürümlerini açıkça oluşturarak bir API'yi revize etmek için bu aşamalı süreç, yeni ve eski kod parçalarının bir süre birlikte var olmasını sağlar.

İşte pratikte bu yaklaşımın iyi (zor) bir örneği. Ben alt parametre bit COUNT bit olmak üzere üçüncü parametre sahip yaklaşım kullanmıştı fonksiyonu BitSubstring () - vardı. C ++ diğer API ve kalıpları ile tutarlı olmak için, işleve argümanlar olarak başlangıç ​​/ bitiş geçmek istedim.

https://github.com/SophistSolutions/Stroika/commit/003dd8707405c43e735ca71116c773b108c217c0

Yeni API ile bir işlev BitSubstring_NEW oluşturdum ve bunu kullanmak için tüm kodumu güncelledim (BitSubString'e NO DAHA FAZLA ARAMA bırakarak). Ancak birkaç sürüm (- ay) için uygulamadan ayrıldım ve kullanımdan kaldırılmış olarak işaretledim - böylece herkes BitSubString_NEW'ye geçebilir (ve o zaman argümanı bir sayıdan başlangıç ​​/ bitiş stiline değiştirebilir).

O zaman - bu geçiş tamamlandığında, BitSubString () 'i silmeyi ve BitSubString_NEW-> BitSubString ()' i yeniden adlandırmayı başka bir taahhütte bulundum (ve BitSubString_NEW adını kaldırdım).


Asla hiçbir anlamı olmayan veya isimlere kendini iten ekler eklemeyin. Her zaman anlamlı isimler vermeye çalışın.
Basilevs

Konuyu tamamen kaçırdınız. Birincisi - bunlar "anlam taşımayan" sonekler değildir. API'nın eskisinden yenisine geçtiği anlamına gelir. Aslında, cevap verdiğim SORU'nun ve yanıtın bütün mesele bu. CLEARLY isimleri, YENİ API ​​olan OLD API olan ve geçiş tamamlandığında sonunda API'nin hedef adı olan iletişim kurar. VE - _OLD / _NEW sonekleri geçicidir - YALNIZCA API değiştirme geçişi sırasında.
Lewis Pringle

Üç yıl sonra API'nın NEW_NEW_3 sürümünde iyi şanslar.
Basilevs
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.