Ünite testini etkinleştirmek için kodumuzu baştan tasarlamalı mıyız?


91

Şu anda ekibimizde ünite testine izin vermek için kod tasarımını değiştirmenin bir kod kokusu olup olmadığı veya bir kod kokusu olmadan ne ölçüde yapılabileceği konusunda tartışmalar var. Bu ortaya çıktı, çünkü sadece diğer tüm yazılım şirketlerinde bulunan uygulamaları uygulamaya koymaya yeni başladık.

Özellikle, çok ince olacak bir Web API servisimiz olacak. Başlıca sorumluluğu, web isteklerini / yanıtlarını toplamak ve iş mantığını içeren temel bir API'yi aramak olacaktır.

Bir örnek, bir kimlik doğrulama yöntemi tipi döndürecek bir fabrika yaratmayı planlıyoruz. Asla olacağı somut türden başka bir şey olacağını tahmin etmediğimizden, bir arayüzü miras almasına gerek yok. Ancak, Web API servisini test etmek için bu fabrika ile alay etmemiz gerekecek.

Bu, temel olarak DI'yi kabul etmek için Web API denetleyici sınıfını tasarladığımız anlamına gelir (yapıcısı veya ayarlayıcısı aracılığıyla); denetleyiciyi bu şekilde tasarlamak zorunda kalmamak için Ninject gibi üçüncü taraf bir çerçeve, ancak yine de bir arayüz oluşturmak zorunda kalacağız.

Ekipteki bazı kişiler sadece test yapmak için kod tasarlama konusunda isteksiz görünüyorlar. Bana öyle geliyor ki, eğer ünite testini yapmayı umuyorsanız, biraz uzlaşmanın olması gerekiyor, ama endişelerinin ne kadar gergin olduğunu bilemiyorum.

Sadece açık olmak gerekirse, bu yepyeni bir proje, bu yüzden birim testini etkinleştirmek için kod değiştirmekle ilgili değil; Bu, test edilebilir bir birim olarak yazacağımız kodu tasarlamakla ilgilidir.


33
Bunu tekrar etmeme izin verin: Siz meslektaşlarınız yeni kod için birim sınamaları istiyorsunuz, ancak mevcut bir şeyi bozma riski olmasa da, kodu test edilebilir bir şekilde yazmayı reddediyorlar mı? Eğer bu doğruysa, @ KilianFoth'un cevabını kabul etmeli ve cevabındaki ilk cümleyi koyu olarak vurgulamasını istemelisin! Meslektaşlarınızın, işlerinin ne olduğu konusunda çok büyük bir yanlış anlama olduğu anlaşılıyor.
Doktor Brown

20
@Lee: Kim ayrılmanın her zaman iyi bir fikir olduğunu söylüyor ? Hiç bir konfigürasyon arayüzü kullanarak bir arayüz fabrikasından yaratılmış bir arayüz olarak her şeyin geçtiği bir kod temeli gördünüz mü? Sahibim; Java ile yazılmış ve tam, elde edilemez, buggy karışıklık oldu. Aşırı ayrılma kod gizlemedir.
Christian Hackl

8
Michael Feathers'ın Eski Kodla Etkili Bir Şekilde Çalışması bu konuyu çok iyi ele alıyor ve yeni bir kod tabanında bile test etmenin avantajları hakkında size iyi bir fikir vermeli.
l0b0

8
@ l0b0 Bu hemen hemen İncil'dir. Stackexchange'te bu sorunun cevabı olmaz, ancak RL'de OP'ye bu kitabı okumasını söylerdim (en azından kısmen). OP, olsun Legacy Kanunu ile Etkin Çalışma ve okumak, en azından kısmen (veya bunu elde etmek için patronuna söyle). Bu gibi soruları ele alıyor. Özellikle test yapmadıysanız ve şimdi teste giriyorsanız - 20 yıllık bir deneyime sahip olabilirsiniz, ancak şimdi deneyimlemediğiniz şeyleri yapacaksınız . Onlar hakkında okumak, deneme yanılma yoluyla dikkatlice hepsini öğrenmekten çok daha kolaydır.
R. Schmitz

4
Michael Feathers'ın kitabının tavsiyesi için teşekkürler, kesinlikle bir kopyasını alacağım.
Lee,

Yanıtlar:


204

Test uğruna kod değiştirme isteksizliği, bir geliştiricinin testlerin rolünü anlamadığını ve uygulamada kuruluştaki kendi rolünü anlamadığını gösterir.

Yazılım işi, işletme değeri yaratan bir kod tabanı sağlama konusunda döner. Acı ve uzun bir deneyimle, test yapmadan önemsiz büyüklükte kod tabanları oluşturamayacağımızı tespit ettik. Bu nedenle, test paketleri işin ayrılmaz bir parçasıdır.

Pek çok kodlayıcı bu ilkeye bağlılık duymaz ancak bilinçaltında asla kabul etmez. Bunun neden olduğunu anlamak kolaydır; Kendi zihinsel kabiliyetimizin sınırsız olmadığı ve aslında, modern bir kod tabanının muazzam karmaşıklığı ile karşı karşıya kaldıklarında şaşırtıcı bir şekilde sınırlı olduğu bilinci, istenmeyen ve kolayca bastırılabilir veya rasyonelleştirilebilir. Test kodunun müşteriye teslim edilmemesi, “temel” işletme koduna kıyasla ikinci sınıf bir vatandaş ve zorunlu olmadığına inanmayı kolaylaştırır. Ve işletme koduna test kodu ekleme fikri pek çok kişiye iki kat rahatsız edici görünüyor.

Bu uygulamanın gerekçelendirilmesinin zorluğu, bir yazılım işinde değerin nasıl yaratıldığının tüm resminin genellikle sadece şirket hiyerarşisindeki yükselişler tarafından anlaşılması gerçeğiyle ilgilidir, ancak bu insanlar teknik olarak ayrıntılı bir anlayışa sahip değillerdir. Testlerin neden elde edilemeyeceğini anlamak için gereken kodlama iş akışı . Bu nedenle, testlerin genel olarak iyi bir fikir olabileceğini garanti eden uygulayıcılar tarafından sıklıkla pasifleştiriliyorlar, ancak "biz böyle koltuk değneklerine ihtiyaç duymayan seçkin programcılarız" ya da "şu an buna vaktimiz yok" vb. İş başarısının bir sayı oyunu olması ve teknik borçlardan kaçınmak, kalite vb. sadece uzun vadede değerini gösterir, bu inançta genellikle çok samimi oldukları anlamına gelir.

Uzun lafın kısası: kodun test edilebilir hale getirilmesi, geliştirme sürecinin önemli bir parçasıdır, diğer alanlardan farklı değildir (birçok mikroçip, yalnızca test amaçlı olarak önemli miktarda öğeyle tasarlanmıştır ), söyledi. Bu tuzağa düşme.


39
Bunun bir çeşit değişime bağlı olduğunu iddia ediyorum. Kodun test edilmesini kolaylaştırma ve ASLA üretimde kullanılmaması gereken teste özgü kancaların tanıtılması arasında bir fark vardır. Ben şahsen sonuncusuna karşı temkinliyim, çünkü Murphy ...
Matthieu M.

61
Birim testler sık ​​sık kapsüllemeyi kırar ve test edildiğinde kodu gerekenden çok daha karmaşık hale getirir (örneğin, ek arayüz türleri ekleyerek veya bayrak ekleyerek). Her zaman olduğu gibi yazılım mühendisliğinde, her iyi uygulama ve her iyi kuralın sorumluluk payına sahiptir. Körü körüne birim testleri bir sürü üreten yapabilirsiniz işin değerine üzerinde zararlı bir etkisi vardır, yani yazma söz ve testler sürdürmek zaten zaman ve emek maliyetleri değil. Tecrübelerime göre, entegrasyon testleri çok daha yüksek bir YG'ye sahiptir ve daha az ödün veren yazılım mimarisini geliştirme eğilimindedir.
Christian Hackl

20
@Lee Elbette, ancak belirli bir test türüne sahip olmanın kod karmaşıklığındaki artışı garanti edip etmediğini düşünmeniz gerekir. Benim kişisel deneyimim, birim testlerinin, temel tasarım değişikliklerini gerektirdikleri noktaya kadar alaycılığa uyum sağlamak için harika bir araç olduğudur. Farklı bir test türüne geçtim. Birim testlerinin yapılması amacıyla mimarlığın büyük ölçüde daha karmaşık hale getirilmesi pahasına birim testleri yazılması, göğe bakıyor.
Konrad Rudolph

21
@HristianHackl, neden bir ünite testi kapsüllemeyi bozuyor? Çalıştığım kod için, testi etkinleştirmek için ekstra işlevsellik eklemek için algılanan bir ihtiyaç varsa , asıl sorun, test etmek istediğiniz işlevin yeniden yapılandırmaya ihtiyaç duymasıdır, yani tüm işlevler aynıdır. soyutlama seviyesi (genellikle ekstra kod için bu "ihtiyacı" yaratan soyutlama düzeyindeki farklılıklar), düşük seviye kodu kendi (test edilebilir) fonksiyonlarına taşınmıştır.
Baldrickk,

29
@HristianHackl Birim testleri, bir birim testinden özel, korumalı veya yerel değişkenlere erişmeye çalışıyorsanız, kapsüllemeyi asla kesmemelidir, yanlış yapıyorsunuzdur. Foo işlevsellikini test ediyorsanız, yalnızca gerçekten çalışıyorsa test edersiniz, yerel x değişkeni, y'nin ikinci döngünün üçüncü yinelemesindeki kare kökü değilse. Bazı işlevler özelse o zaman öyle olsun, yine de geçiş testi yapacaksınız. gerçekten büyük ve özel mi? Bu bir tasarım hatasıdır, ancak başlık uygulama ayrımıyla C ve C ++ dışında bile mümkün değildir.
opa

75

Düşündüğün kadar basit değil. Yıkalım.

  • Ünite testleri yazmak kesinlikle iyi bir şey.

FAKAT!

  • Kodunuzda yapılacak herhangi bir değişiklik bir hataya neden olabilir. Bu nedenle, kodu iyi bir iş nedeni olmadan değiştirmek iyi bir fikir değildir.

  • 'Çok ince' webapi üniteniz için en iyi durum gibi görünmüyor.

  • Kod ve testleri aynı anda değiştirmek kötü bir şey.

Aşağıdaki yaklaşımı öneririm:

  1. Entegrasyon testlerini yaz . Bu herhangi bir kod değişikliği gerektirmemelidir. Size temel test durumlarınızı verecektir ve yaptığınız herhangi bir kod değişikliğinin herhangi bir hata ortaya koymadığını kontrol etmenizi sağlayacaktır.

  2. Yeni kodun test edilebilir olduğundan ve birim ve entegrasyon testlerinin olduğundan emin olun .

  3. CI zincirinizin yapı ve dağıtımlardan sonra testler yaptığından emin olun.

Bunları kurduğunuzda, ancak daha sonra test edilebilirlik için eski projeleri yeniden düzenlemeyi düşünmeye başlayın.

Umarım herkes süreçten dersler çıkarır ve testin en çok nerede gerekli olduğu, onu nasıl yapılandırmak istediğiniz ve işletmeye kazandırdığı değer hakkında iyi bir fikir sahibi olur.

EDIT : Bu cevabı yazdığımdan beri, OP mevcut kodda yapılan değişikliklerden değil yeni koddan bahsettiğini göstermek için soruyu netleştirdi. Belki de saf bir şekilde "Ünite testi iyi midir?" Diye düşündüm. tartışma birkaç yıl önce çözüldü.

Birim testlerinde hangi kod değişikliklerinin gerekli olacağını hayal etmek zor, ancak her durumda isteyeceğiniz genel iyi uygulama değildir. Muhtemelen gerçek itirazları incelemek akıllıca olacaktır, muhtemelen itiraz edilen birim test tarzıdır.


12
Bu, kabul edilenden çok daha iyi bir cevap. Oylardaki dengesizlik dehşet vericidir.
Konrad Rudolph

4
@ Bir ünite testi , bir sınıfa karşılık gelen veya vermeyen bir işlevsellik ünitesini test etmelidir . Arayüzünde bir işlevsellik birimi test edilmelidir (bu durumda API olabilir). Test, tasarım kokularını ve bazı farklı / daha fazla seviyelendirme uygulamalarının gerekliliğini vurgulayabilir. Sistemlerinizi küçük kompost parçalardan kurun, mantıklı olması ve test edilmesi daha kolay olacaktır.
Wes Toleman 3

2
@KonradRudolph: OP'nin bu sorunun mevcut kodu değiştirmeden yeni kod tasarlamakla ilgili olduğunu eklediği noktayı kaçırdım. Yani kırılacak bir şey yok, bu cevabın çoğunu uygulanamaz hale getiriyor.
Doktor Brown,

1
Birim test yazmanın her zaman iyi bir şey olduğu ifadesine kesinlikle katılmıyorum. Ünite testleri sadece bazı durumlarda iyidir. Ön uç (UI) kodunu test etmek için birim testleri kullanmak aptalca, işletme mantığını test etmek için yapılmıştır. Ayrıca, eksik derleme kontrollerini değiştirmek için birim testleri yazmak iyidir (örn. Javascript'te). Yalnızca ön uç kodunun, ünite testleri değil, yalnızca uçtan uca test yazması gerekir.
Sulthan

1
Tasarımlar kesinlikle "Test kaynaklı hasar" dan muzdarip olabilir. Genellikle test edilebilirlik tasarımı geliştirir: Bir şey alınamayacağı ancak geçilmesi gereken testler yazarken, daha net arayüzler ve benzeri şeyler için dikkat edin. Ancak bazen, yalnızca test için rahatsız edici bir tasarım gerektiren bir şeyle karşılaşırsınız . Bir örnek, örneğin bir singleton kullanan mevcut üçüncü taraf kodu nedeniyle, yeni kodunuzda gereken yalnızca test yapıcı olabilir. Bu gerçekleştiğinde: bir adım geriye atıp test edilebilirlik adına kendi tasarımınıza zarar vermek yerine yalnızca bir entegrasyon testi yapın.
Anders Forsgren

18

Kodun doğal olarak test edilebilir olması için tasarlanması kod kokusu değil; Aksine, iyi bir tasarımın işaretidir. Büyük bir avantaj olarak kolay (daha kolay) test sunan, buna dayanan (örneğin, Model-View-Presenter) bazı iyi bilinen ve yaygın olarak kullanılan tasarım desenleri vardır.

Bu nedenle, daha kolay bir şekilde test etmek için somut sınıfınız için bir arayüz yazmanız gerekirse, bu iyi bir şeydir. Zaten somut bir sınıfınız varsa, çoğu IDE ondan bir arayüz çıkararak çabayı minimum düzeyde tutar. İkisini senkronize etmek biraz daha fazla iş, ancak bir arayüzün yine de fazla değişmemesi gerekiyor ve testten elde edilen faydalar bu ekstra çabadan daha ağır basabilir.

Öte yandan, @MatthieuM olarak. Bir yorumda, kodunuza üretimde kullanılmaması gereken, yalnızca test yapmak amacıyla kullanılmaması gereken belirli giriş noktaları ekliyorsanız, bu bir sorun olabilir.


Bu sorun statik kod analizi ile çözülebilir - yöntemleri işaretleyin (örneğin adlandırılmalıdır _ForTest) ve test dışı koddan gelen çağrılar için kod tabanını kontrol edin.
Riking,

13

IMHO, birim testleri oluşturmak için test edilecek kodun en azından belirli özelliklere sahip olması gerektiğini anlamak çok basittir. Örneğin, kod yalıtılmış bir şekilde test edilebilecek ayrı birimlerden oluşmuyorsa, "birim test" kelimesi bir anlam ifade etmiyor bile. Kod bu özelliklere sahip değilse, önce değiştirilmesi gerekir, bu oldukça açıktır.

Teoride, ilk önce bazı SOLID ilkelerini uygulayarak bazı test edilebilir kod birimlerini yazmayı deneyebilir ve daha sonra orijinal kodu değiştirmeden bir test yazmaya çalışabilirsiniz. Ne yazık ki, gerçekten test edilebilir bir kod yazmak her zaman basit değildir, bu nedenle yalnızca testleri oluşturmaya çalışırken tespit edebilecek bazı değişiklikler yapılması muhtemeldir. Bu, ünite testi düşünülerek yazıldığında bile kod için geçerlidir ve "ünite test edilebilirliğinin" başlangıçta gündemde olmadığı yerlerde yazılan kod için kesinlikle daha doğrudur.

Önce ünite testlerini yazarak sorunu çözmeye çalışan iyi bilinen bir yaklaşım var - buna Test Driven Development (TDD) denir ve kodun en baştan test edilebilir hale getirilmesi kesinlikle yardımcı olabilir.

Tabii ki, daha sonra kodun test edilebilir hale getirilmesi için değiştirilmesinin isteksizliği, genellikle kodun ilk önce manuel olarak test edildiği ve / veya prodcution'da iyi çalıştığı bir durumda ortaya çıkar, bu nedenle değiştirmek gerçekten de yeni hataları ortaya çıkarabilir, bu doğru. Bunu hafifletmek için en iyi yaklaşım ilk önce bir regresyon test paketi oluşturmaktır (genellikle kod bazında sadece çok az değişiklikle uygulanabilir) ve ayrıca kod incelemeleri veya yeni manuel test oturumları gibi eşlik eden diğer tedbirler. Bazı iç organların yeniden tasarlanmasının önemli hiçbir şeyi bozmadığından emin olmak için yeterince güvenmeniz gerekiyor.


TDD'den bahsettiğin ilginç. Bazı dirençle de karşılaşan BDD / TDD'yi getirmeye çalışıyoruz - yani "geçirilecek minimum kod" gerçekten ne anlama geliyor?
Lee

2
@Lee: Bir organizasyona değişiklik getirmek her zaman bir miktar direnişe neden olur ve her zaman yeni şeyleri uyarlamak için biraz zamana ihtiyaç duyar, bu yeni bir bilgelik değildir. Bu bir insan problemi.
Doktor Brown

Kesinlikle. Keşke daha fazla zamanımız olsaydı!
Lee

Bu, insanlara bu şekilde yapmanın zaman kazandıracağını (ve umarım çabucak) de göstermesi sık sık söz konusudur . Neden sana yararı olmayacak bir şey yapıyorsun?
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersen: Ekip aynı şekilde OP'ye de yaklaşımlarının zaman kazandıracağını gösterebilir. Kim bilir? Ancak, burada daha az teknik nitelikte sorunlarla karşı karşıya gelmediğimizi merak ediyorum; OP, ekibinin neyi yanlış yaptığını bize söylemeye devam ediyor (sanırım), sanki davası için müttefik bulmaya çalışıyormuş gibi. Projeyi Stack Exchange'deki yabancılarla değil ekiple birlikte tartışmak daha faydalı olabilir .
Christian Hackl

11

Yaptığınız (doğrulanmamış) iddia ile konuya katılıyorum:

Web API servisini test etmek için bu fabrika ile alay etmemiz gerekecek

Bu mutlaka doğru değil. Testleri yazmak için birçok yol vardır ve orada olan mocks içermeyen birim testleri yazmak için yollar. Daha önemlisi, işlevsel veya entegrasyon testleri gibi başka tür testler de vardır. Çoğu zaman bir OOP programlama dili olmayan bir "arayüzde" bir test dikişi "bulmak mümkündür interface.

Daha doğal olabilen alternatif bir test dikişi bulmanıza yardımcı olacak bazı sorular:

  • Farklı bir API üzerinden ince bir Web API yazmak isteyecek miyim ?
  • Web API ile temel API arasındaki kod çoğaltmasını azaltabilir miyim? Biri diğeri açısından üretilebilir mi?
  • Tüm Web API'sini ve temel API'yi tek bir "kara kutu" birim olarak değerlendirebilir ve her şeyin nasıl davrandığı hakkında anlamlı bir şekilde iddiada bulunabilir miyim?
  • Web API'sinin gelecekte yeni bir uygulamayla değiştirilmesi gerekiyorsa, bunu nasıl yaparız?
  • Gelecekte Web API'sinin yerine yeni bir uygulama geldiyse, Web API'sinin müşterileri bunu fark eder mi? Öyleyse nasıl?

Yaptığınız bir başka doğrulanmamış iddia DI ile ilgili:

Web API denetleyici sınıfını DI'yi (yapıcısı veya ayarlayıcısı aracılığıyla) kabul edecek şekilde tasarlıyoruz; bu, denetleyicinin bir bölümünü yalnızca DI izin vermek ve aksi takdirde gerek duymadığımız bir arabirim uygulamak için tasarlıyoruz ya da üçüncü bir taraf kullanıyoruz. Ninject gibi bir yapı, denetleyiciyi bu şekilde tasarlamaktan kaçınmak için yine de bir arayüz oluşturmak zorunda kalacağız.

Bağımlılık enjeksiyonu mutlaka yeni bir şey yaratmak anlamına gelmez interface. Örneğin, bir kimlik doğrulama belirteci nedeninde: Programlı bir şekilde gerçek bir kimlik doğrulama belirteci oluşturabilir misiniz ? Daha sonra test böyle belirteçler oluşturabilir ve bunları enjekte edebilir. Bir belirteci doğrulama işlemi bir tür şifreleme sırrına mı dayanıyor? Umarım bir sırrı kodlamamışsındır - umarım bir şekilde depodan okuyabilirsin ve bu durumda test vakalarında farklı (iyi bilinen) bir sır kullanabilirsin.

Bu asla yeni bir şey yaratmaman gerektiğini söylemek değildir interface. Ancak, bir test yazmak için tek bir yol veya bir davranış sergilemek için tek bir yol olmaktan kaçının. Kutunun dışında olduğunu düşünüyorsanız, genellikle kodunuzun minimum şekilde kısıtlanmasını gerektiren ve yine de istediğiniz efekti veren bir çözüm bulabilirsiniz.


Arayüzlerle ilgili iddiaları ele alın, ancak onları kullanmasak bile, yine de bazı nesneleri enjekte etmemiz gerekecek, bu takımın geri kalanının endişesi. yani takımdaki bazı kişiler, somut uygulamayı somutlaştıran ve buna bırakan parametresiz bir ctr ile mutlu olacaklardır. Aslında bir üye, dalgaları enjekte etmek için yansıma kullanma fikrinden yola çıktı, bu yüzden onları kabul etmek için kod tasarlamak zorunda değiliz. Hangi reeky bir kod kokusu imo
Lee,

9

Bu yeni bir proje olduğu için şanslısınız. Test Driven Design'ın iyi kod yazmak için çok iyi çalıştığını tespit ettim (bu yüzden ilk başta yapıyoruz).

Endam ile ön gerçekçi girdi verileri ile bir kod verilmiş parça çağırmak ve sonra amaçlandığı gibi, sen çok erken sürecinde API tasarımı yapmak olduğunu kontrol edebilirsiniz gerçekçi çıkış verilerini almak ve bir alma şanslarının yüksek olduğu nasıl kullanışlı tasarım, uyum sağlamak için yeniden yazılması gereken mevcut kodla engellenmediğiniz için. Ayrıca akranlarınız tarafından anlaşılması kolaydır, böylece sürecin başlarında tekrar iyi tartışmalar yapabilirsiniz.

Yukarıdaki cümleydeki "kullanışlı" ifadesinin yalnızca ortaya çıkan yöntemlerin çağrılmasının kolay olduğu değil, aynı zamanda entegrasyon testlerine katılması kolay temiz arayüzler alma ve makbuz yazma eğiliminde olduğunuz anlamına da dikkat edin.

Bir düşün. Özellikle akran değerlendirmesi ile. Tecrübelerime göre zaman ve emek yatırımı çok hızlı bir şekilde iade edilecektir.


TDD ile de bir sorunumuz var: “geçmek için asgari kod”. Takıma bu süreci gösterdim ve sadece önceden tasarladığımız şeyi yazma konusunda bir istisna yaptılar - ki anlayabiliyorum. "Asgari" tanımlanmış görünmüyor. Bir test yazarsak ve net plan ve tasarımlarımız varsa, neden testi geçmesi için yazmıyorsunuz?
Lee,

@Lee "geçmek için minimum kod" ... iyi, bu biraz aptalca gelebilir, ama tam olarak ne diyor. Örneğin, eğer bir testiniz varsa UserCanChangeTheirPassword, o zaman testte (henüz mevcut değil) fonksiyonunu şifrenizi değiştirmek için çağırırsınız ve daha sonra şifrenin gerçekten değiştirildiğini iddia edersiniz. Ardından, işlevi test edebilirsiniz ve ne istisnalar atmaz ne de yanlış bir iddiaya girinceye kadar işlevi yazarsınız. Bu noktada herhangi bir kod eklemek için bir nedeniniz varsa, o zaman bu sebep başka bir teste girer, örn UserCantChangePasswordToEmptyString.
R. Schmitz,

@En Sonunda, testleriniz, yalnızca kağıt üzerinde mürekkep yerine yerine getirilip getirilmediğini kontrol eden belgeler dışında, kodunuzun ne yaptığının belgelenmesi olur. Ayrıca bu soru ile karşılaştırın - CalculateFactorialSadece 120 döndüren ve test başarılı olan bir yöntem . Yani bir az. Ayrıca belli ki amaçlanan bu değildir, ama bu sadece ne ifade etmek başka bir test gerektiği anlamına gelmektedir edildi niyetindeydi.
R. Schmitz,

1
@Lee Küçük adımlar. Çıplak minimum, kod önemsiz seviyenin üstüne çıktığında düşündüğünüzden daha fazla olabilir. Ayrıca, her şeyi bir kerede uygularken yaptığınız tasarım yine daha az optimal olabilir çünkü daha önce bunu gösteren testleri yazmadan nasıl yapılması gerektiği konusunda varsayımlarda bulunuyorsunuz. Yine unutmayın, kod ilk önce başarısız olmalıdır.
Thorbjørn Ravn Andersen

1
Ayrıca, regresyon testleri çok önemlidir. Takım için kapsamdalar mı?
Thorbjørn Ravn Andersen

8

Kodda değişiklik yapmanız gerekirse, kod kokusu budur.

Kişisel deneyimlerime göre, kodum için testler yazmak zorsa, kod kötüdür. Kötü kod değil çünkü tasarlandığı gibi çalışmıyor veya çalışmıyor, kötü çünkü neden çalıştığını hemen anlayamıyorum. Eğer bir hatayla karşılaşırsam, bunu düzeltmek için çok acı verici bir iş olacağını biliyorum. Kodun tekrar kullanılması zor ve imkansız.

İyi (Temiz) kod, görevleri bir bakışta (veya en azından iyi bir görünüşte) kolayca anlaşılan daha küçük bölümlere ayırır. Bu küçük bölümleri test etmek kolaydır. Ayrıca, alt bölümler hakkında oldukça emin olduğumda yalnızca kod tabanının bir kısmını test eden testler yazabilirim (yeniden kullanım, daha önce test edildiği gibi burada da yardımcı olur).

Kodu, test edilmesi kolay, yeniden yapılandırılması kolay ve baştan tekrar kullanmak kolaydır; değişiklik yapmanız gerektiğinde kendinizi öldürmezsiniz.

Tamamen temiz bir prototip olması gereken bir projeyi tamamen temiz bir kod haline getirirken bunu da yazıyorum. Kısmen işe yarayan bir şeyi kırma korkusuyla herhangi bir şeye dokunmaktan korkmaktan, saatlerce bir ekrana bakmak yerine, en baştan ve refactor kötü kodundan en kısa zamanda elde etmek çok daha iyidir.


3
"Atılabilir prototip" - her bir proje hayata asla başlamaz - şeyleri asla olmadığı gibi düşünmek için. Bunu benim gibi yazarak .. tahmin et ne oldu? ... olmadığı ortaya çıkan bir atış prototipinin
yenilenmesi

4
Atılacak bir prototipin atılacağından emin olmak istiyorsanız, bunu üretimde asla izin verilmeyen bir prototip dilinde yazın. Clojure ve Python iyi seçeneklerdir.
Thorbjørn Ravn Andersen

2
@ ThorbjørnRavnAndersen Bu beni kıkırdattı. Bu, bu dillerde kazmak anlamına mı geliyordu? :)
Lee

@Lee. Hayır, dillerin sadece örnek olabilecek üretime uygun olmayabilir - organizasyonda kimse onları korumak çünkü genellikle onlarla aşina oldukları için ve onların öğrenme eğrileri dik. Eğer bunlar kabul edilebilir ise, olmayan birini seçin.
Thorbjørn Ravn Andersen

4

Birim test edilemeyen kod yazmanın kod kokusu olduğunu iddia ediyorum . Genel olarak, kodunuz ünite test edilemiyorsa, modüler değildir, bu da anlaşılmasını, sürdürülmesini veya geliştirilmesini zorlaştırır. Belki kod, yalnızca entegrasyon testi açısından anlamlı olan bir tutkal koduysa, ünite testi için entegrasyon testinin yerine geçebilirsiniz, ancak entegrasyon başarısız olduğunda bile sorunu izole etmeniz gerekecektir ve ünite testi harika bir yoldur. yap.

Diyorsun

Bir kimlik doğrulama yöntemi türü döndürecek bir fabrika oluşturmayı planlıyoruz. Asla olacağı somut türden başka bir şey olacağını tahmin etmediğimizden, bir arayüzü miras almasına gerek yok. Ancak, Web API servisini test etmek için bu fabrika ile alay etmemiz gerekecek.

Bunu gerçekten takip etmiyorum. Bir şey yaratan bir fabrikaya sahip olmanın nedeni fabrikaları değiştirmenize ya da fabrikanın kolayca ne yarattığını değiştirmenize izin vermektir, bu nedenle kodun diğer bölümlerinin değişmesine gerek yoktur. Kimlik doğrulama yönteminiz asla değişmeyecekse, fabrika işe yaramaz kod şişmesidir. Bununla birlikte, testte üretimden farklı bir kimlik doğrulama yöntemine sahip olmak istiyorsanız, testte üretimden farklı bir kimlik doğrulama yöntemi döndüren bir fabrikaya sahip olmak harika bir çözümdür.

Bunun için DI veya Mocks'a ihtiyacınız yok. Farklı kimlik doğrulama türlerini desteklemesi ve bir şekilde bir yapılandırma dosyasındaki veya ortam değişkenindeki gibi bir şekilde yapılandırılabilmesi için fabrikanıza ihtiyacınız var.


2

Aklıma gelen her mühendislik disiplininde, iyi veya daha yüksek kalite seviyelerine ulaşmanın tek yolu var:

Tasarımda muayene / test yapmak için.

Bu, inşaat, talaş tasarımı, yazılım geliştirme ve imalatta geçerlidir. Şimdi, bu, testlerin her tasarımın değil inşa edilmesi gereken dayanak olduğu anlamına gelmez. Ancak, her tasarım kararında, tasarımcıların test maliyetlerine etkileri konusunda net olmalı ve ticaret ile ilgili bilinçli kararlar almalıdırlar.

Bazı durumlarda, manuel veya otomatik (örneğin Selenyum) testi, tek başına kabul edilebilir test kapsamı sağlarken, Birim Testlerinden daha uygun olacaktır. Nadir durumlarda, neredeyse tamamen denenmemiş bir şeyi oraya atmak da kabul edilebilir. Ancak bunların, dava kararlarıyla bilinçli olması gerekir. Bir "kod kokusu" testini hesaba katan bir tasarıma çağrı yapılması ciddi bir deneyim eksikliğine işaret ediyor.


1

Birim testinin (ve diğer otomatik test türlerinin) kod kokularını azaltma eğiliminde olduğunu ve kod kokularını ortaya koydukları tek bir örnek düşünemiyorum. Birim testleri genellikle sizi daha iyi kod yazmaya zorlar. Kolayca test edilen bir yöntemi kullanamıyorsanız, neden kodunuzda daha kolay olmalı?

İyi yazılmış birim testleri, kodun nasıl kullanılması gerektiğini size gösterir. Bunlar çalıştırılabilir bir belge şeklidir. Tuhaf bir şekilde yazılı, anlaşılamayan aşırı uzun testler gördüm. Bunları yazma! Sınıflarınızı ayarlamak için uzun sınavlar yazmanız gerekirse, sınıflarınızın yeniden düzenlemeye ihtiyacı vardır.

Birim testleri, kodunuzun bir kısmının nerede olduğunu vurgulayacaktır. Michael C. Feathers'ın Legacy Code ile Etkili Bir Şekilde Çalışmasını okumanızı tavsiye ederim . Projeniz yeni olsa bile, zaten (veya çok fazla) birim testine sahip değilse, kodunuzu güzel bir şekilde test etmek için açık olmayan bazı tekniklere ihtiyacınız olabilir.


3
Test edebilmek için çok sayıda dolaylı katman tanıtmak ve daha sonra beklendiği gibi kullanmamaya özen gösterebilirsiniz.
Thorbjørn Ravn Andersen

1

Kısaca:

Test edilebilir kod (genellikle) bakım kodudur - ya da test edilmesi zor olan kodun bakımı genellikle zordur . Test edilebilir olmayan kod tasarlama, tamir edilemeyen bir makine tasarlamaya benzer - sonunda tamir etmesi için atanacak zavallı mırıldanmaya acıyın (o da siz olabilirsiniz).

Bir örnek, bir kimlik doğrulama yöntemi tipi döndürecek bir fabrika yaratmayı planlıyoruz. Asla olacağı somut türden başka bir şey olacağını tahmin etmediğimizden, bir arayüzü miras almasına gerek yok.

Üç yıl içinde beş farklı kimlik doğrulama yöntemi türüne ihtiyacınız olacağını biliyorsunuz, şimdi söylediniz, değil mi? Gereksinimler değişir ve tasarımınızı değiştirmekten kaçınmanız gerekirken, test edilebilir bir tasarıma sahip olmak, tasarımınızın (sadece) acı çekmeden değiştirilebilecek yeterli dikişlere sahip olduğu anlamına gelir; değişikliklerin hiçbir şeyi bozmaz.


1

Bağımlılık enjeksiyonunu tasarlamak kod kokusu değildir - en iyi yöntemdir. DI kullanımı sadece test edilebilirlik için değildir. Bileşenlerinizi DI etrafında inşa etmek modülerlik ve yeniden kullanılabilirlik sağlar, ana bileşenlerin (örneğin bir veritabanı arayüz katmanı gibi) değiştirilmesine daha kolay olanak sağlar. Bir dereceye kadar karmaşıklık eklerken, doğru yapılması, katmanların daha iyi ayrılmasını ve karmaşıklığın yönetilmesini ve gezinmesini kolaylaştıran işlevsellik izolasyonunun ayrılmasını sağlar. Bu, her bir bileşenin davranışını doğru bir şekilde doğrulamayı kolaylaştırır, hataları azaltır ve ayrıca hataları izlemeyi kolaylaştırır.


1
"doğru yapılması" bir problemdir. DI'nin yanlış yapıldığı iki projeyi sürdürmek zorundayım (“doğru” yapmayı amaçlasa da). Bu, kodu DI ve ünite testi olmadan eski projelerden daha korkunç ve daha kötü hale getirir. DI'yi doğru yapmak kolay değildir.
Ocak

@Jan bu ilginç. Nasıl yanlış yaptılar?
Lee,

1
@Lee One projesi hızlı bir başlangıç ​​zamanı gerektiren bir hizmettir ancak başlangıçta korkunç derecede yavaştır çünkü tüm sınıf başlatma DI çerçevesi (C # 'daki Windsor Windsor) tarafından önceden yapılmıştır. Bu projelerde gördüğüm bir diğer sorun da DI'yi "yeni" olan nesnelerin yaratılmasıyla DI'yi karıştırmak. Bu, tekrar test etmeyi zorlaştırır ve bazı kötü yarış koşullarına yol açar.
Ocak

1

Bu, temel olarak DI'yi kabul etmek için Web API denetleyici sınıfını tasarladığımız anlamına gelir (yapıcısı veya ayarlayıcısı aracılığıyla); denetleyiciyi bu şekilde tasarlamak zorunda kalmamak için Ninject gibi üçüncü taraf bir çerçeve, ancak yine de bir arayüz oluşturmak zorunda kalacağız.

Bir test edilebilir arasındaki farka bakalım:

public class MyController : Controller
{
    private readonly IMyDependency _thing;

    public MyController(IMyDependency thing)
    {
        _thing = thing;
    }
}

ve test edilemeyen kontrolör:

public class MyController : Controller
{
}

Eski seçenek, ikisi de Visual Studio tarafından otomatik olarak oluşturulabilen 5 ekstra kod satırı içeriyor. Bağımlılık enjeksiyon çerçevenizi IMyDependencyçalışma zamanında somut bir türle değiştirecek şekilde ayarladıktan sonra - herhangi bir iyi DI çerçevesi için, başka bir tek kod satırı olan - Just Just Works ile ilgili her şey, şimdi kontrol cihazınızı kalbinizin içeriğine göre alay edebilir ve test edebilirsiniz. .

Test edilebilirliğe izin vermek için 6 ekstra kod satırı ... ve meslektaşlarınız bunun "çok fazla iş" olduğunu mu savunuyorlar? Bu tartışma benimle uçmuyor ve seninle uçmamalı.

Ve yok oluşturmak ve test için bir arabirim uygulamak zorunda: Moq , örneğin, birim test amaçlı beton türünün davranışını simüle etmek için izin verir. Tabii ki, bu tipleri test ettiğiniz sınıflara enjekte edemezseniz, bu size pek faydası olmaz.

Bağımlılık enjeksiyonu, bir kez anladığınızda, "bu olmadan nasıl çalıştım?" Diye merak ettiğiniz şeylerden biri. Çok basit, etkili ve sadece Mantıklı. Lütfen, meslektaşlarınızın projenizi test edilebilir kılma yolunda yeni şeyler anlamadaki eksikliklerine izin vermeyin.


1
“Yeni şeyleri anlama konusundaki eksiklik” olarak işten çıkarmanın çok hızlı olduğu şey , eski şeylerin iyi bir şekilde anlaşılması olabilir. Bağımlılık enjeksiyonu kesinlikle yeni değil. Fikir ve muhtemelen en eski uygulamalar, onlarca yıllık. Ve evet, cevabınızın ünite testi nedeniyle daha karmaşık hale gelen bir kod örneği ve muhtemelen kapsüllemeyi kıran ünite testi örneği olduğuna inanıyorum (çünkü sınıfta ilk önce bir kamu kurucusu olduğunu kim söylüyor?). Bağımlılık enjeksiyonunu, takaslar nedeniyle başka birinden miras aldığım kod tabanlarından kaldırdım.
Christian Hackl

Kontrolörler her zaman bir kamu kurucusuna sahiptir, dolaylı veya açık, çünkü MVC bunu gerektirir. "Karmaşık" - belki, inşaatçıların nasıl çalıştığını anlamadıysanız. Kapsülleme - bazı durumlarda evet, ancak DI - kapsülleme tartışması, burada yardım etmeyecek devam eden, son derece öznel bir konudur ve özellikle çoğu uygulama için DI, kapsülleme IMO'sundan daha iyi hizmet edecektir.
Ian Kemp,

Kamu kurucuları ile ilgili olarak: gerçekten, kullanılan çerçevenin bir özelliğidir. Bir çerçeve ile somutlaştırılmayan sıradan bir sınıfın daha genel bir vakasını düşünüyordum. Neden ilave karmaşıklık olarak ek yöntem parametreleri görmenin, inşaatçıların nasıl çalıştığı hakkında bir anlayış eksikliğine neden olduğuna inanıyorsunuz? Bununla birlikte, DI ile kapsülleme arasındaki bir tradeofun varlığını kabul ettiğinizi takdir ediyorum.
Christian Hackl

0

Birim testleri yazarken, kodumda neyin yanlış gidebileceğini düşünmeye başladım. Kod tasarımını geliştirmeme ve tek sorumluluk ilkesini (SRP) uygulamama yardımcı oluyor . Ayrıca, birkaç ay sonra aynı kodu değiştirmeye geldiğimde, mevcut işlevselliğin bozulmadığını doğrulamama yardımcı oluyor.

Saf işlevleri kullanabildiğiniz kadar kullanma eğilimi vardır (sunucusuz uygulamalar). Birim testi durumu izole etmeme ve saf fonksiyonlar yazmama yardımcı oluyor.

Özellikle, çok ince olacak bir Web API servisimiz olacak. Başlıca sorumluluğu, web isteklerini / yanıtlarını toplamak ve iş mantığını içeren temel bir API'yi aramak olacaktır.

Önce ilgili API için birim testleri yazın ve yeterli geliştirme süreniz varsa, ince Web API hizmeti için de testler yazmanız gerekir.

TL; DR, birim testi kod kalitesini yükseltmeye ve gelecekteki kod riskinde değişiklik yapmanıza yardımcı olur. Aynı zamanda kodun okunabilirliğini de arttırır. Amacınızı belirtmek için yorumlar yerine testleri kullanın.


0

Sonuç olarak ve isteksiz partiyle argümanınız ne olmalı, hiçbir çelişki olmaması. Büyük hata, birinin testten nefret eden insanlara "test için tasarım yapma" fikrini oluşturduğu görülüyor. Ağızlarını kapatmalı ya da "bunu doğru yapmak için zaman ayıralım" gibi farklı bir şekilde söylemeliydiler.

Test edilebilir bir şey yapmak için "bir arayüz uygulamanız gerekir" fikri yanlış. Arabirim zaten uygulandı, henüz sınıf bildirgesinde ilan edilmedi. Mevcut kamu yöntemlerini tanımak, imzalarını bir arayüze kopyalamak ve bu arayüzü sınıfın beyanında ilan etmek meselesidir. Programlama yok, mevcut mantıkta değişiklik yok.

Görünüşe göre bazı insanlar bu konuda farklı bir fikir var. Öncelikle bunu düzeltmeyi denemenizi öneririm.

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.