TDD Kırmızı-Yeşil-Refaktörü ve özel hale getirilmiş yöntemleri test etmek için nasıl / nasıl


91

Anladığım kadarıyla, çoğu insan özel yöntemlerin doğrudan test edilmemesi gerektiği, bunun yerine kamusal yöntemler ne şekilde olursa olsun denenmesi gerektiği konusunda hemfikir. Onların noktasını görebiliyorum, ancak "TDD'nin Üç Yasasını" izlemeye çalıştığımda ve "Kırmızı - yeşil - refaktör" döngüsünü kullanmaya başladığımda bazı problemlerim var. Bence en iyi örnekle açıklanıyor:

Şu anda, bir dosyayı okuyabilen (sekmeyle ayrılmış veriler içeren) ve sayısal olmayan veriler içeren tüm sütunları filtreleyebilen bir programa ihtiyacım var. Sanırım bunu yapmak için halihazırda bazı basit araçlar var, ancak onu baştan çizmeye karar verdim, çünkü çoğunlukla TDD ile pratik yapmanın güzel ve temiz bir proje olacağını düşündüm.

Yani, ilk önce "kırmızı şapkayı taktım", yani, başarısız bir teste ihtiyacım var. Sayısal olmayan tüm alanları bir çizgide bulan bir yönteme ihtiyacım olacağını düşündüm. Bu yüzden basit bir test yazıyorum, tabii ki derhal derlenemiyor, bu yüzden fonksiyonun kendisini yazmaya başlıyorum ve bir kaç ileri ve geri (kırmızı / yeşil) döngüden sonra bir çalışma fonksiyonuna ve tam bir teste sahibim.

Sonra, her seferinde bir satır dosya okuyan ve bir satırda okuyan "gatherNonNumericColumns" işleviyle devam ediyorum ve sonunda çıkarılması gereken tüm sütunları toplamak için her satırdaki "findNonNumericFields" işlevimi çağırıyorum. Birkaç kırmızı-yeşil çevrimli ve yine bir çalışma fonksiyonu ve tam bir test olan bittim.

Şimdi yeniden düşünmeliyim. "FindNonNumericFields" yöntemim yalnızca "gatherNonNumericColumns" uygularken buna ihtiyacım olacağını düşündüğüm için tasarlandığından, "findNonNumericFields" öğesinin özel olmasına izin vermenin makul olacağını düşünüyorum. Ancak bu, ilk testlerimi bozacak, çünkü test ettikleri yönteme artık ulaşamayacaklardı.

Bu yüzden, özel bir yöntem ve onu test eden bir test paketi ile bitirdim. Birçok insan özel yöntemlerin test edilmemesi gerektiğini tavsiye ettiğinden, burada kendimi bir köşeye boyattım gibi geliyor. Ama tam olarak nerede başarısız oldum?

Daha yüksek bir düzeyde başlayabilirdim, nihayetinde kamu yöntemim haline geleceğini test eden bir test yazdım (yani, FindAndFilterOutAllNonNumericalColumns), ancak bu TDD'nin tüm noktasına biraz karşı geliyor (en azından Bob Amca'ya göre). : Yazma testleri ve üretim kodu arasında sürekli geçiş yapmalısınız ve herhangi bir zamanda, tüm testleriniz son dakika içinde çalıştı. Çünkü bir kamu yöntemi için test yazarak başlarsam, testin kamuoyunu test etmesi için özel yöntemlerde tüm detayları öğrenmeden önce birkaç dakika (veya çok karmaşık durumlarda saatlerce veya hatta günler) olacak yöntem geçer.

Peki ne yapmalı? TDD (hızlı kırmızı-yeşil-refactor çevrimi ile) sadece özel yöntemlerle uyumlu değil mi? Yoksa tasarımımda bir hata mı var?



2
Bu iki işlev parçası farklı birimler için yeterince farklıdır - bu durumda özel yöntemler muhtemelen kendi sınıflarında olmalıdır - ya da aynı birimdir, bu durumda neden testler yazdığınızı göremiyorum ünite içindeki davranış . Son paragraf ile ilgili olarak, çatışmayı göremiyorum. Bir test senaryosundan geçmek için neden tümüyle karmaşık bir özel yöntem yazmanız gerekiyor? Neden bunu kamuya açık bir yöntemle yavaş yavaş kullanmayalım ya da satır içi ile başlatıp sonra çıkartmıyorsunuz?
Ben Aaronson

26
Neden insanlar, programlamanın benden daha iyi olduğu konusunda gerçek kılavuzlar olarak, programlama kitaplarından ve bloglardan deyimler ve klişeler alıyorlar.
AK_

7
TDD'yi tam da bu sebepten dolayı sevmiyorum: Yeni bir bölgedeyseniz, mimarlığın nasıl olması gerektiğini ve bazı şeylerin nasıl çalıştığını öğrenmeye çalışırken çok fazladan fazla çalışma yapacaksınız. Öte yandan: Zaten deneyimlediğiniz bir bölgedeyseniz, o zaman testleri sinir bozucu bir kenara bırakmanın yanı sıra, intellisense neden derlenemeyen kodları yazdığınızı anlamıyor demektir. Tasarım hakkında düşünme, yazma ve daha sonra test etme konusunda çok daha büyük bir hayranıyım.
Jeroen Vannevel

1
"çoğu insan özel yöntemlerin doğrudan test edilmemesi gerektiği konusunda hemfikir gibi görünüyor" - hayır, bir yöntemi doğrudan denemek uygunsa test edin. Olarak sakla privatebunu yapmak için mantıklı olmadığı.
osa

Yanıtlar:


44

Birimler

Sorunun tam olarak nerede başladığını tam olarak tespit edebilirim:

Sayısal olmayan tüm alanları bir çizgide bulan bir yönteme ihtiyacım olacağını düşündüm.

Bunu hemen kendinize sormanız gerekir: " gatherNonNumericColumnsAynı olanın bir parçası mı yoksa bir parçası olacak mı?"

Eğer cevap " evet, ayrı " ise, hareket tarzınız basittir: bu yöntemin uygun bir sınıfta halka açık olması gerekir, bu yüzden bir birim olarak test edilebilir. Zihniyetiniz, "Bir yöntemi denemeye ihtiyacım var, ayrıca başka bir yöntemi de test etmeye ihtiyacım var" gibi bir şey.

Yine de söylediklerine göre, cevabın " hayır, aynı şeyin" olduğunu düşündün . Bu noktada, planınız artık tam olarak yazmak, findNonNumericFields sonra da yazmak olmak olmamalıdır gatherNonNumericColumns. Bunun yerine, basitçe yazmak gerekir gatherNonNumericColumns. Şimdilik, findNonNumericFieldsbir sonraki kırmızı test durumunuzu seçerken ve yeniden düzenleme yaparken aklınızdaki yerin muhtemel bir parçası olmalıdır. Bu kez zihniyetiniz "Bir yöntemi test etmeyi test etmem gerekiyor ve bunu yaparken de bitmiş uygulamamın muhtemelen bu diğer yöntemi içereceğini göz önünde bulundurmalıyım" dır .


Kısa bir döngü tutmak

Gerektiğini yukarıda yapmak değil size sondan bir önceki paragrafta tarif sorunlara neden:

Çünkü bir kamu yöntemi için test yazarak başlarsam, testin kamuoyunu test etmesi için özel yöntemlerde tüm detayları öğrenmeden önce birkaç dakika (veya çok karmaşık durumlarda saatlerce veya hatta günler) olacak yöntem geçer.

Hiçbir şekilde bu teknik, yalnızca tamamını findNonNumericFieldssıfırdan uyguladığınızda yeşile dönecek olan kırmızı bir test yazmanızı gerektirmez . Çok daha büyük olasılıkla, findNonNumericFieldsbirkaç döngü boyunca oluşturulacak ve nihayetinde bir yeniden yapılanma işlemi sırasında çıkacak olan, test ettiğiniz genel yöntemde bir kod olarak başlayacaktır.


Yol Haritası

Bu belirli örnek için yaklaşık bir yol haritası vermek için, kullandığınız test durumlarını tam olarak bilmiyorum, ancak gatherNonNumericColumnsgenel yönteminiz olarak yazdığınızı söyleyin . O zaman büyük olasılıkla test senaryoları findNonNumericFields, her biri yalnızca bir satır içeren bir tablo kullanarak yazdıklarınızla aynı olacaktır . Bu tek satırlık senaryo tam olarak uygulandığında ve sizi metodu çıkarmaya zorlamak için bir test yazmak istediğinizde, yinelemenizi eklemenizi gerektiren iki satırlık bir dava yazarsınız.


2
Bence buradaki cevap bu. TDD'yi bir OOP ortamında benimsemekle, kendimi sık sık kendi aşağıdan gelen içgüdülerimin üstesinden gelmekte zorlanıyorum. Evet, işlevler küçük olmalıdır, ancak yeniden yapılanmadan sonra. Önceleri büyük monolitler olabilirler. +1
João Mendes

2
@ JoãoMendes Pekala, yeniden yapılanma işleminden önce, özellikle de çok kısa RGR çevrimlerinde devasa bir monolit durumuna geçmeniz gerektiğinden emin değilim. Fakat evet, test edilebilir bir ünite içerisinde aşağıdan yukarıya çalışmak OP'nin tanımladığı sorunlara yol açabilir.
Ben Aaronson

1
Tamam, sanırım şimdi nerede yanlış gittiğini anlıyorum. Hepinize çok teşekkür ederim (bunu cevap olarak işaretledik, ancak diğer cevapların çoğu da aynı derecede faydalıdır)
Henrik Berg

66

Birçok insan birim testinin metot temelli olduğunu düşünüyor; değil. Mantıklı olan en küçük birim etrafında kurulmalıdır. Çoğu şey için bu, sınıfın bütün bir varlık olarak test etmeniz gereken şey olduğu anlamına gelir. Bireysel yöntemler değil.

Şimdi açık bir şekilde sınıfa yöntemler çağırıyor olacaksınız, ancak testleri karaliz nesnesine uyguladığınızı düşünmelisiniz, bu nedenle sınıfınızın sağladığı mantıksal işlemleri görebilmelisiniz; bunlar test etmeniz gereken şeyler. Sınıfınız mantıksal işlemin çok karmaşık olduğu kadar büyükse, önce düzeltilmesi gereken bir tasarım sorununuz vardır.

Bin yöntemli bir sınıf test edilebilir görünebilir, ancak her yöntemi tek tek test ederseniz, sınıfı gerçekten test etmiyorsunuzdur. Bazı sınıflar, bir yöntem çağrılmadan önce, örneğin veri göndermeden önce bağlantı kurması gereken bir ağ sınıfı gibi belirli bir durumda olmalarını gerektirebilir. Veri gönderme yöntemi tüm sınıftan bağımsız olarak kabul edilemez.

Bu nedenle, özel yöntemlerin testle alakasız olduğunu görmelisiniz. Özel yöntemlerinizi sınıfınızın genel arayüzünü arayarak uygulayamıyorsanız, bu özel yöntemler işe yaramaz ve yine de kullanılamaz.

Bence pek çok insan özel yöntemleri test edilebilir birimlere dönüştürmeye çalışıyor, çünkü onlar için test yapmak kolay görünüyor, ancak bu testin ayrıntı derecesini çok fazla alıyor. Martin Fowler diyor

Birimin sınıf olduğu nosyonuyla başlasam da, sık sık birbiriyle yakından ilgili dersler alıyorum ve onları tek bir ünite olarak görüyorum.

Nesne yönelimli bir sistem için çok anlamlı, nesneler birim olarak tasarlandı. Tek tek metotları test etmek istiyorsanız, belki de C gibi bir prosedür sistemi ya da tamamen statik fonksiyonlardan oluşan bir sınıf oluşturmalısınız.


14
Bana göre bu cevap OP'nin sorusundaki TDD yaklaşımını tamamen görmezden geliyor. Sadece "Özel yöntemler test etmeyin" mantra tekrarı, ancak TDD nasıl açıklamıyor - olduğu aslında yöntem bazlı - olmayan bir yöntem bazlı birim test yaklaşımıyla işe yarayabilecek.
Doktor Brown

6
@DocBrown hayır, ünitelerini "fazla küçümseme" diyerek tamamen kendine cevap veriyor ve hayatı kendine zorlaştırıyor. TDD olduğunu değil bazlı yöntem, edilmektedir birim bir birim ne olursa olsun mantıklı olduğu tabanlı. Bir C kütüphaneniz varsa, evet, her birim bir işlev olacaktır. Bir sınıfınız varsa, birim bir nesnedir. Fowler'ın dediği gibi, bazen bir birim sıkıca ilgili birkaç sınıftır. Bence çoğu kişi, bazı serseriler metotlara dayanarak taslaklar ürettiği için ünite testlerini metot metodu olarak görüyor.
gbjbaanb

3
@gbjbaanb: OP'nin "sayısal olmayan alanları bir satırda toplamasını", daha önce yazmayı düşündüğü sınıfın ortak arayüzüne sahip olmadan, ilk önce saf TDD kullanarak gerçekleştirmesine izin veren bir test önermeyi deneyin.
Doktor Brown,

8
Burada @DocBrown ile aynı fikirdeyim. Sorgunun sorunu, özel yöntemleri test etmeden elde edebileceğinden daha fazla test ayrıntı düzeyi istediği için değildir. Bu, sıkı bir TDD yaklaşımını izlemeye çalıştığı ve böyle bir plan yapmadan, bir anda özel bir yöntem olması gerektiği için bir sürü test yaptırdığı bir duvara çarpmasına neden oldu. Bu cevap bu konuda yardımcı olmuyor. Bazı sorulara güzel bir cevap, sadece bu değil.
Ben Aaronson

7
@Matthew: Yanılgısı, işlevi ilk etapta yazmasıydı. İdeal olarak, kamu yöntemini spagetti kodu olarak yazmalı ve daha sonra refactor döngüsünde özel bir işleve yeniden yansıtmalıdır - refactor döngüsünde özel olarak işaretlememelidir.
slebetman

51

Veri toplama yöntemlerinin testleri hak edecek kadar karmaşık olması ve bazı hedeflerin bir kısmından ziyade kendi amaçlarına göre kendi asıl hedefinden ayrılmasının çözüme götürdüğü gerçeği : bu yöntemleri özel değil , diğer sınıfın üyeleri yapın. Bu toplama / filtreleme / tablolama işlevselliği sağlar.

Ardından, yardımcı sınıfın aptal veri-munging yönleri için testler (örneğin, "karakterlerden rakamları ayırma") ve başka bir yerde birincil hedefiniz için testler (örneğin, "satış rakamlarını elde etme"), ve iş mantığınız için yapılan testlerde temel filtreleme testlerini tekrar etmeniz gerekmiyor.

Oldukça genel olarak, bir şeyi yapan sınıfınız, birincil amacı için gerekli olan ancak bundan ayrı bir şey yapmak için kapsamlı bir kod içeriyorsa, bu kod başka bir sınıfta yaşamalı ve ortak yöntemlerle çağrılmalıdır. Yanlışlıkla bu kodu içeren bir sınıfın özel köşelerinde gizlenmemelidir . Bu, aynı zamanda test edilebilirliği ve anlaşılabilirliği geliştirir.


Evet ben size katılıyorum. Fakat hem ilk ifadenle, hem "yeterince karmaşık" hem de "yeterince ayrı" kısımlarıyla ilgili bir sorunum var. "Yeterince karmaşık" ile ilgili olarak: Hızlı bir kırmızı-yeşil döngüsü yapmaya çalışıyorum, bu da teste geçmeden önce (veya başka bir şekilde) bir anda en fazla bir dakika kadar kod yazabileceğim anlamına geliyor. Bu benim testleri gerçekten çok iyi taneli olacak anlamına gelir. Bunun TDD'nin avantajlarından biri olduğunu düşündüm, ama belki fazla abarttım, bu yüzden dezavantaj olur.
Henrik Berg,

"Yeterince ayırmak" ile ilgili olarak: (yine amcadan gelen) fonksiyonların küçük ve onlardan daha küçük olması gerektiğini öğrendim. Yani temelde 3-4 satır işlevi yapmaya çalışıyorum. Bu nedenle, ne kadar küçük ve basit olursa olsun, tüm işlevsellik az çok basit bir şekilde kendi yöntemlerine ayrılır.
Henrik Berg,

Her neyse, veri aktarma yönlerinin (örneğin, findNonNumericFields) gerçekten özel olması gerektiğini düşünüyorum. Ve onu başka bir sınıfa ayırırsam, yine de herkese açık hale getirmek zorunda kalacağım, bu yüzden bu konuyu tam olarak göremiyorum.
Henrik Berg,

6
@HenrikBerg, neden en başta nesneleriniz olduğunu düşünür - işlevleri gruplamak için uygun yollar değildir, ancak karmaşık sistemleri çalışmayı kolaylaştıran bağımsız birimlerdir. Bu nedenle, sınıfı bir şey olarak test etmeyi düşünmelisiniz.
gbjbaanb

@gbjbaanb İkisinin de aynı olduğunu iddia ediyorum.
RubberDuck

29

Şahsen, testleri yazarken uygulama zihniyetinde ileri gittiğinizi hissediyorum. Sen kabul belirli yöntemler gerekir. Ama sınıfın yapması gerekeni yapmaları için onlara gerçekten ihtiyacın var mı? Birisi bir araya gelerek ve onları içeriden ayırdıysa sınıf başarısız olur mu? Sınıfı kullanıyor olsaydınız (ve bence test cihazının zihniyeti olmalı), sayıları kontrol etmek için açık bir yöntem olup olmadığını gerçekten daha az önemseyebilirsiniz.

Bir sınıfın ortak arayüzünü test etmelisiniz. Özel uygulama bir nedenden dolayı özeldir. Bu, kamu arayüzünün bir parçası değildir, çünkü gerekli değildir ve değişebilir. Bir uygulama detayı.

Genel arayüze karşı testler yazarsanız, karşılaştığınız sorunu asla çözemezsiniz. İsterseniz özel yöntemlerinizi kapsayan (harika) genel arabirim için test senaryoları oluşturabilirsiniz veya olamaz. Bu durumda, özel yöntemler hakkında çok fazla düşünmek ve yine de ulaşılamaması durumunda hepsini tamamen hurdaya çıkarmak zaman olabilir.


1
"Uygulama detayları", "değişkenler arasında geçiş yapmak için bir XOR veya geçici değişken kullandım mı" gibi şeylerdir. Korumalı / özel yöntemlerin başka şeyler gibi sözleşmeleri vardır. Belli kısıtlamalar altında girdi alıyorlar, onunla çalışıyorlar ve bir miktar çıktı üretiyorlar. Sonunda sözleşmesi olan herhangi bir şey test edilmelidir - mutlaka kütüphanenizi tüketenler için değil, bunu sürdüren ve sizin için değiştiren kişiler için. Sırf "genel" olmadığı için bir API’nin parçası olmadığı anlamına gelmez .
Knetic

11

Sınıfın dahili olarak ne yapmasını beklediğinizi temel alarak TDD yapmazsınız.

Test durumlarınız, sınıf / işlevsellik / programın dış dünyaya yapması gerekenlere dayanmalıdır. Örnekte, kullanıcı hiç okuyucunuzu sınıfla arayacak mı?find all the non-numerical fields in a line?

Cevap "hayır" ise, ilk etapta yazmak kötü bir sınavdır. Testin işlevsellik üzerine bir sınıf / arayüz düzeyinde yazmasını istiyorsunuz - "sınıf metodu bunun işe yaraması için ne yapmalı" seviyesine değil, testiniz budur.

TDD'nin akışı:

  • kırmızı (dış dünyaya sınıf / nesne / fonksiyon / etc ne yapıyor?)
  • yeşil (bu dış dünya işlevinin çalışması için minimum kodu yazın)
  • refactor (bu işi yapmak için daha iyi kod nedir)

Bunu yapmak DEĞİLDİR "çünkü gelecekte X'e özel bir yöntem olarak ihtiyacım olacak, uygulayalım ve önce test edeyim." Kendinizi bunu yaparken buluyorsanız, yanlış olarak "kırmızı" aşamayı yapıyorsunuzdur. Buradaki senin sorunun gibi görünüyor.

Kendinizi sık sık özel yöntemler haline getiren yöntemler için sınamalar bulursanız, birkaç şeyden birini yapıyorsunuz:

  • Arabiriminizi / genel düzeydeki kullanım durumlarınızı doğru bir şekilde anlamadığınızdan, bunlar için bir test yazabilecek kadar
  • Tasarımınızı önemli ölçüde değiştirmek ve birkaç testi tekrar gözden geçirmek (bu işlevselliğin yeni testlerde test edilip edilmediğine bağlı olarak iyi bir şey olabilir)

9

Genel olarak test etme ile ilgili ortak bir yanılgı ile karşılaşıyorsunuz.

Test etmeye yeni başlayan çoğu insan şu şekilde düşünmeye başlar:

  • F işlevi için bir test yaz
  • F uygulayın
  • G fonksiyonu için bir test yaz
  • F çağrısını kullanarak G uygulamasını
  • H işlevi için bir test yaz
  • G çağrısını kullanarak H uygulayın

ve bunun gibi.

Buradaki problem aslında H fonksiyonu için birim testiniz olmamasıdır. H test etmesi gereken test aslında H, G ve F'yi aynı anda test ediyor.

Bunu çözmek için, test edilebilir birimlerin asla birbirlerine değil, arayüzlerine bağlı olmaları gerektiğini anlamalısınız . Durumda, ünitelerin basit fonksiyonlar olduğu durumlarda, arayüzler sadece çağrı imzalarıdır. Bu nedenle G'yi, F ile aynı imzayı taşıyan herhangi bir fonksiyonla kullanılabilecek şekilde uygulamalısınız .

Bunun tam olarak nasıl yapılabileceği programlama dilinize bağlıdır. Birçok dilde işlevleri (veya işaretçileri) diğer işlevlerin argümanları olarak iletebilirsiniz. Bu, her işlevi ayrı ayrı test etmenize olanak sağlar.


3
Keşke bunu daha çok oy kullanabilseydim. Çözümünüzü doğru bir şekilde tasarlamadığınız için özetlerdim.
Justin Ohms,

C gibi bir dilde, bu mantıklı geliyor, ancak ünitenin genel olarak bir sınıf olması gereken OO dilleri için (genel ve özel yöntemlerle), o zaman sınıftaki her özel yöntemi yalıtmak yerine test etmelisiniz. Sınıflarını izole etmek, evet. Her sınıftaki metotların yalıtılması, no.
gbjbaanb

8

Teste Dayalı Geliştirme sırasında yazdığınız testlerin, bir sınıfın genel API'sini doğru bir şekilde uyguladığından ve aynı zamanda genel API'nin test edilmesinin ve kullanımının kolay olduğundan emin olması gerekir.

Elbette bu API'yi uygulamak için özel yöntemler kullanabilirsiniz, ancak TDD yoluyla testler oluşturmanıza gerek yoktur - genel API düzgün çalışacağından işlevsellik test edilecektir.

Şimdi, kişisel metotlarınızın bağımsız testlere layık olacak kadar karmaşık olduğunu varsayalım - ancak orijinal sınıfınızın genel API'sinin bir parçası olarak bir anlam ifade etmiyorlar. Eh, bu muhtemelen onların başka bir sınıftaki ortak yöntem olması gerektiği anlamına gelir - birincisi, asıl sınıfınızın kendi uygulamasında yararlandığı bir yöntem.

Yalnızca genel API'yi test ederek, gelecekte uygulama ayrıntılarını değiştirmeyi çok daha kolaylaştırıyorsunuz. Yardımcı olmayan testler, daha sonra keşfettiğiniz bazı zarif yeniden düzenlemeyi desteklemek için yeniden yazılmaları gerektiğinde sizi rahatsız edecektir.


4

Bence doğru cevap, kamu yöntemleriyle başlamakla ilgili elde ettiğiniz sonuçtur. Bu yöntemi çağıran bir test yazarak başlarsınız. Bu başarısız olur, bu nedenle hiçbir şey yapmadan bu ada sahip bir yöntem yaratırsınız. Sonra belki bir dönüş değeri kontrol eden bir testte hak olabilirsiniz.

(İşlevlerinizin ne yaptığı konusunda tam olarak net değilim. Sayısal olmayan değerlerin çıkarıldığı dosya içeriğinde bir dize döndürüyor mu?)

Metodunuz bir string döndürürse, o dönüş değerini kontrol edin. Yani sadece onu inşa etmeye devam ediyorsun.

Özel bir yöntemle gerçekleşen herhangi bir şeyin, işleminiz sırasında bir noktada ortak yöntemde olması gerektiğini düşünüyorum ve sonra yalnızca yeniden düzenleme adımının bir parçası olarak özel yönteme taşındı. Yeniden yapılanma, bildiğim kadarıyla başarısız testlerden geçmeyi gerektirmiyor. İşlevsellik eklerken yalnızca başarısız testlere ihtiyacınız var. Hepsinin geçmesini sağlamak için yeniden ateşleyiciden sonra testlerinizi yapmanız yeterli.


3

Burada kendimi bir köşeye boyadım gibi geliyor. Ama tam olarak nerede başarısız oldum?

Eski bir atasözü var.

Planlamayı başaramazsan, planın başarısız olur.

İnsanlar TDD'nizde sadece oturup, testler yazdığınızı ve tasarımın sihirli bir şekilde gerçekleşeceğini düşünüyor gibi görünüyor. Bu doğru değil. Yüksek düzeyde bir planın olması gerekir. İlk önce arayüzü (genel API) tasarlarken TDD'den en iyi sonuçları aldığımı öğrendim. Şahsen, önce interfacesınıfı tanımlayan bir gerçek yaratıyorum .

gasp Herhangi bir test yapmadan önce bazı "kod" yazdım! Hayır, hayır. Yapmadım İzlenecek bir sözleşme yazdım , bir tasarım . Grafik kağıdındaki UML diyagramını not ederek benzer sonuçlar elde edebileceğinizden şüpheleniyorum. Mesele şu ki, bir planın olmalı. TDD, bir kod parçasına saldırmaktan kurtulacak bir lisans değildir.

"Önce Test" bir yanlış isim gibi hissediyorum. Önce Tasarım, sonra test et.

Elbette, lütfen başkalarının kodunuzdan daha fazla sınıf çıkarmak için verdikleri tavsiyelere uyun. Bir sınıfın iç kısımlarını test etme zorunluluğunu hissederseniz, bu iç kısımları kolayca test edilebilecek bir birime çıkartın ve enjekte edin.


2

Unutmayın ki testler de yeniden canlandırılabilir! Bir yöntemi özel yaparsanız, genel API'yi düşürürsünüz ve bu nedenle bu "kaybedilen işlevsellik" (AKA azaltılmış karmaşıklık) için ilgili bazı testleri atmak tamamen kabul edilebilir.

Diğerleri, özel yönteminizin ya diğer API testlerinizin bir parçası olarak çağrılacağını ya da erişilemeyeceğini ve dolayısıyla silinebileceğini söyledi. Aslında, yürütme yollarını düşünürsek işler daha iyi anlaşılır .

Örneğin, bölünme gerçekleştiren bir genel yöntemimiz varsa, sıfıra bölme ile sonuçlanan yolu test etmek isteyebiliriz. Biz yöntemi özel yaparsak, bir seçim olsun: ya biz bölünme-by-sıfıra yolunu düşünebiliriz, ya biz diğer yöntemlerle denir nasıl dikkate alarak bu yolu ortadan kaldırabilir.

Bu şekilde, bazı testlerden vazgeçebiliriz (örneğin, sıfıra bölme) ve kalan genel API açısından diğerlerini yeniden değerlendirebiliriz. Tabii ki, ideal bir dünyada, mevcut testler kalan tüm yollarla ilgilenir, ancak gerçeklik her zaman bir uzlaşmadır;)


1
Diğer cevaplar özel yöntemin kırmızı döngüye yazılmaması gerektiği konusunda doğruysa da insanlar hata yapar. Ve hata yolunda yeterince ileri gittiğinde, bu uygun bir çözümdür.
slebetman

2

Özel bir yöntemin başka bir sınıfın genel yöntemi haline getirilebileceği zamanlar vardır.

Örneğin, iş parçacığı güvenliği olmayan özel sınıflarınız olabilir ve sınıfı geçici bir durumda bırakabilirsiniz. Bu yöntemler, birinci sınıfınız tarafından özel olarak tutulan ayrı bir sınıfa taşınabilir. Bu nedenle, sınıfınız bir Queue ise, genel yöntemleri olan bir InternalQueue sınıfınız olabilir ve Queue sınıfı, InternalQueue örneğini özel olarak tutar. Bu, dahili kuyruğu test etmenize izin verir ve ayrıca Dahili Kuyruktaki bireysel işlemlerin ne olduğunu da netleştirir.

(List sınıfının olmadığını hayal ettiğinizde ve List işlevlerini bunları kullanan sınıfta özel yöntemler olarak uygulamaya çalıştıysanız, bu en açık olanıdır.)


2
“Özel bir yöntemin başka bir sınıfın ortak bir yöntemi haline getirilebileceği zamanlar vardır.” Bunu yeterince vurgulayamıyorum. Bazen özel bir yöntem, kendi kimliği için çığlık atan başka bir sınıftır.

0

Dilinizin neden sadece herkese açık ve tamamen özel olmak üzere iki gizlilik düzeyi olduğunu merak ediyorum.

Herkese açık olmayan yöntemlerinizi pakete erişilebilir ya da bunun gibi bir şeyle düzenleyebilir misiniz? Ardından, testlerinizi aynı pakete yerleştirin ve ortak arabirimin bir parçası olmayan iç işleri test etmenin tadını çıkarın. Derleme sisteminiz bir sürüm ikili oluştururken testleri hariç tutacaktır.

Elbette bazen tanımlayıcı sınıftan başka hiçbir şey için erişilebilir olmayan, gerçekten özel yöntemlere ihtiyaç duyabilirsiniz. Umarım tüm bu yöntemler çok küçüktür. Genel olarak yöntemleri küçük tutmak (örneğin 20 satırın altında) çok yardımcı olur: test, bakım ve sadece kodu anlamak kolaylaşır.


3
Yalnızca testler yapmak için bir yöntem erişim değiştiricisini değiştirmek, bir kuyruğun bir köpeğin kıpırdadığı bir durumdur. Birimin iç kısımlarını test etmek, daha sonra yeniden refaktör yapmayı zorlaştırıyor. Genel arayüzü test etmek, aksine, bir ünite için “sözleşme” olarak çalıştığı için harika.
scriptin

Bir yöntemin erişim seviyesini değiştirmeniz gerekmez. Söylemeye çalıştığım, testler de dahil olmak üzere belirli bir kodun kamuya açık bir sözleşme yapmadan daha kolay yazılmasına izin veren orta düzeyde bir erişim seviyeniz olduğu. Tabii ki , genel arayüzü sınamak zorundasınız, ancak ek olarak, iç çalışmaların bir kısmını yalıtılmış olarak test etmek bazen yararlı olabilir.
9000

0

Zaman zaman daha hassas taneli testlere izin vermek için korunan özel metotlara rastladım (maruz kalan herkese açık API'dan daha sıkı). Bu, kuraldan ziyade (umarım çok nadir) bir istisna olmalıdır, ancak karşılaşabileceğiniz belirli belirli durumlarda yardımcı olabilir. Ayrıca, genel bir API oluştururken hiç düşünmek istemediğiniz bir şey, bu nadir durumlarda dahili kullanım yazılımı üzerinde kullanabileceğiniz "hile" den fazlası.


0

Bunu deneyimledim ve acınızı hissettim.

Benim çözümüm şunlardı:

bir monolith inşa etmek gibi testleri tedavi etmeyi bırakın.

Bir takım testler yazdığınızda, bazı işlevleri yerine getirmek için 5 diyelim, tüm bu testleri sürdürmeniz gerekmediğini unutmayın. , özellikle de başka bir şeyin bir parçası olduğunda, .

Örneğin, sık sık var:

  • düşük seviye testi 1
  • karşılamak için kod
  • düşük seviye testi 2
  • karşılamak için kod
  • düşük seviye testi 3
  • karşılamak için kod
  • düşük seviye testi 4
  • karşılamak için kod
  • düşük seviye testi 5
  • karşılamak için kod

öyleyse bende var

  • düşük seviye testi 1
  • düşük seviye testi 2
  • düşük seviye testi 3
  • düşük seviye testi 4
  • düşük seviye testi 5

Şimdi testlerin bir şey var ki o dediğimiz üst düzey işlev (ler) ekleyin Ancak, ben belki şimdi sadece olmak için bu düşük seviyeli testlerini azaltmak mümkün:

  • düşük seviye testi 1
  • düşük seviye testi 5

Şeytan ayrıntıda gizlidir ve bunu yapabilme yeteneği koşullara bağlı olacaktır.


-2

Güneş dünyanın etrafında mı yoksa dünyanın etrafında güneşin etrafında mı döner? Einstein'a göre cevap evet ya da her iki model de sadece bakış açısına göre farklılık gösterdiğinden, aynı şekilde kapsülleme ve teste dayalı gelişim, sadece sandığımız için çelişkili olduğu için. Burada Galileo ve papa gibi oturup, birbirimize hakaret etmekle uğraşıyoruz: aptal, özel yöntemlerin de test etmesi gerektiğini görmüyor musun? heretic, kapsülleme kırma! Aynı şekilde, hakikatin düşündüğümüzden daha büyük olduğunu fark ettiğimizde, özel arayüzler için yapılan testlerin kapsüllenmesi gibi bir şey deneyebilir miyiz ki, genel arayüzler için yapılan testler de enkapsülasyonu bozmaz.

Bunu deneyin: iki yöntem ekleyin, biri girişi olmayan, ancak justs özel test sayısını döndürür, diğeri parametre olarak test numarasını alır ve pass / fail değerini döndürür.


1
Seçilen hakaretler Galileo ve Papa tarafından bu soruya verilen cevaplar tarafından kullanılmamıştır.
hildred
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.