TDD: Sıkıca bağlanmış nesneleri alay etmek


10

Bazen nesnelerin sıkıca bağlanması gerekir. Örneğin, bir CsvFilesınıf muhtemelen CsvRecordsınıfla (veya ICsvRecordarabirimle) sıkı bir şekilde çalışmalıdır .

Ancak geçmişte öğrendiğim kadarıyla, test odaklı geliştirmenin ana ilkelerinden biri "Asla bir seferde birden fazla sınıfı test etmektir". Yani ICsvRecordgerçek örnekleri yerine alay veya saplamalar kullanmalısınız CsvRecord.

Ancak bu yaklaşımı denedikten sonra, CsvRecordsınıftan alay etmenin biraz kıllı olabileceğini fark ettim . Bu da beni iki sonuçtan birine götürüyor:

  1. Birim testleri yazmak zor! Bu bir kod kokusu! Elden Geçirme!
  2. Her bağımlılığın üstesinden gelmek mantıksızdır.

Alaylarımı gerçek CsvRecordörneklerle değiştirdiğimde işler çok daha sorunsuz geçti. Diğer insanların düşüncelerine bakarken , yukarıdaki # 2'yi desteklediği anlaşılan bu blog gönderisine rastladım . Doğal olarak sıkıca bağlı nesneler için alay konusunda çok endişelenmemeliyiz.

Pist dışında mıyım? Yukarıda # 2 varsayımının bir dezavantajı var mı? Aslında tasarımımı yeniden düzenlemeyi düşünmeli miyim?


1
Ben "birim testleri" "birim" mutlaka bir sınıf olması yaygın bir yanlış anlama olduğunu düşünüyorum. Örneğin, bu iki sınıfın bir birim oluşturmasının daha iyi olabileceği bir durum olduğunu düşünüyorum. Ama beni yanlış anlamayın, tamamen Robert Harvey'nin cevabına katılıyorum.
Doc Brown

Yanıtlar:


11

Bu iki sınıf arasında gerçekten koordinasyona ihtiyacınız varsa CsvCoordinator, iki sınıfınızı kapsayan bir sınıf yazın ve bunu test edin.

Ancak, CsvRecordbağımsız olarak test edilemeyen düşünceye itiraz ediyorum . CsvRecordtemelde bir DTO sınıfı, değil mi? Bu sadece birkaç yardımcı yöntemle bir alan koleksiyonu. Ve CsvRecorddiğer bağlamlarda da kullanılabilir CsvFile; CsvRecordörneğin bir koleksiyonunuz veya diziniz olabilir .

CsvRecordÖnce test edin . Tüm testlerini geçtiğinden emin olun. Sonra devam edin ve test sırasında sınıfınızla CsvRecordbirlikte kullanın CsvFile. Önceden test edilmiş bir saplama / sahte olarak kullanın; ilgili test verileriyle doldurun, iletin CsvFileve test durumlarınızı buna karşı yazın.


1
Evet, CsvRecord kesinlikle bağımsız olarak test edilebilir. Sorun, CsvRecord'da bir şey kırılırsa, CsvData testlerinin başarısız olmasına neden olacaktır. Ama bunun büyük bir sorun olduğunu düşünmüyorum.
Phil

1
Bence bunun olmasını istiyorsun. :)
Robert Harvey

1
@RobertHarvey: Teorik olarak, CsvRecord ve CsvFile oldukça karmaşık sınıflar haline geliyorsa ve CsvFile için bir test kırılırsa, artık CsvFile veya CsvRecord'da bir sorun olup olmadığını hemen bilmiyorsunuz. Ama sanırım bu daha varsayımsal bir durum - eğer böyle sınıfları gerçek dünya programı için programlama görevim olsaydı, bunu tam olarak tarif ettiğiniz şekilde yapardım.
Doc Brown

2
@Phil: Eğer CsvRecordkırılırsa, o zaman açıkça CsvDatabaşarısız olur; ama bu sorun değil, çünkü CsvRecordönce testi yaparsınız ve başarısız olursa CsvFiletestleriniz anlamsızdır. İçerideki CsvRecordve içindeki hataları birbirinden ayırabilirsiniz CsvFile.
tdammers

5

Her seferinde bir sınıfı test etmenin nedeni, bir sınıfın testlerinin ikinci bir sınıfın davranışına bağımlı olmasını istememenizdir. Bu, A Sınıfı testiniz B Sınıfı işlevlerinden herhangi birini uygularsa, B Sınıfı'ndaki belirli işlevlere bağımlılığı kaldırmak için B Sınıfı ile dalga geçmeniz gerektiği anlamına gelir.

CsvRecordBana benzer bir sınıf , çoğunlukla veri depolamak için olduğu gibi - kendi başına çok fazla işlevselliğe sahip bir sınıf değil. Yani, yapıcılar, alıcılar, ayarlayıcılar olabilir, ancak gerçek önemli mantığı olan hiçbir yöntemi yoktur. Tabii ki, burada tahmin ediyorum - belki CsvRecordçok sayıda karmaşık hesaplamalar yapan bir sınıf yazdınız .

Ama eğer CsvRecordkendine ait gerçek bir mantığı yoksa, alay ederek kazanılacak hiçbir şey yoktur. Bu gerçekten sadece eski özdeyiştir - "değer nesnelerini taklit etme" .

Bu nedenle, belirli bir sınıfın alay edilip edilmeyeceğini düşünürken (farklı bir sınıfın testi için), sınıfın kendi mantığının ne kadarına sahip olduğunu ve testiniz sırasında bu mantığın ne kadarının yürütüleceğini dikkate almalısınız.


+1. Sonucu birden fazla nesnenin davranışının doğruluğuna bağlı olan herhangi bir test, birim test değil, bir entegrasyon testidir. Gerçek bir birim testi almak için bu nesnelerden birini alay etmelisiniz. Bu sadece gerçek davranışları olmayan nesneler için geçerli değildir - örneğin sadece alıcılar ve ayarlayıcılarla.
guillaume31

1

Hayır. # 2 iyidir. Yapılacaklar olabilir ve olmalıdır onların kavramları sıkı bağlı olup olmadığını sıkı bağlı. Bu nadir ve genellikle kaçınılmalıdır, ancak verdiğiniz örnekte mantıklıdır.


0

"Birleştirilmiş" sınıflar karşılıklı olarak birbirine bağımlıdır. Tanımladığınız şey bu olmamalıdır - bir CsvRecord onu içeren CsvFile'ı gerçekten önemsememelidir, bu nedenle bağımlılık sadece bir yöne gider. Bu iyi ve sıkı bağlantı değil .

Sonuçta, bir sınıf String adı değişkenini içeriyorsa, bunun String'e sıkıca bağlı olduğunu iddia etmezsiniz, değil mi?

Bu nedenle, birim CsvRecord'u istenen davranışı açısından test eder.

Ardından, ünitenizin doğru şekilde bağlı olduğu nesnelerle etkileşime girip girmediğini test etmek için bir alaycı çerçeve kullanın (Mockito harika). Test etmek istediğiniz davranış gerçekten - CsvFile CsvRcord'ları beklenen şekilde işler. CvsRecord'un iç işleri önemli değil - CvsFile bununla iletişim kurar.

Son olarak, TDD sadece birim testlerle ilgili değildir . Kesinlikle daha büyük bileşenlerinizin nasıl çalıştığına dair işlevsel davranışa, yani kullanıcı hikayenize veya senaryonuza bakan işlevsel testlerle başlayabilirsiniz (ve yapmalısınız). Birim testleriniz beklentileri belirler ve parçaları doğrular, fonksiyonel testler bütünü için aynısını yapar.


1
-1, sıkı bağlantı mutlaka döngüsel bağımlılıklar anlamına gelmez, bu bir yanlış anlamadır. Örnek olarak, CsvFile bir sızdırmaz bir şekilde bağlanmış CsvRecord(fakat tam tersi). İyi bir test için bir fikir ise OP sorar CsvFileonu katlanarak CsvRecordbir ile ICsvRecorddeğil, tersi.
Doc Brown

2
@DocBrown: Kaplinin sıkı olup olmadığı, dosyanın CsvFileiç işleyişine CsvRecord, yani dosyanın kayıt hakkındaki varsayım miktarına ne kadar bağlı olduğuna bağlıdır . Arayüzler bu tür varsayımların belgelenmesine ve uygulanmasına yardımcı olur (daha doğrusu diğer varsayımların yokluğu), ancak bir arayüzle farklı bir kayıt sınıfını bağlayabilmeniz dışında bağlantı miktarı aynı kalır CsvFile. Arayüzün tanıtılması, böylece daha az kuplajın olduğunu söyleyebilmeniz saçmadır.
tdammers

0

Burada gerçekten iki soru var. Birincisi, bir nesnenin alay edilmesinin tavsiye edilemediği durumlar varsa. Diğer mükemmel cevapların gösterdiği gibi, bu kuşkusuz doğrudur. İkinci soru, özel davanızın bu durumlardan biri olup olmadığıdır. Bu soruya ikna olmadım.

Muhtemelen bir sınıfı alay etmemenin en yaygın nedeni, bir değer sınıfı olmasıdır. Ancak, kuralın arkasındaki nedene bakmalısınız. Alaycı sınıf bir şekilde kötü olacağı için değil, esas olarak orijinaliyle aynı olacağı için. Eğer durum böyle olsaydı, orijinal sınıfı kullanarak birim testiniz daha kolay olmazdı.

Kodunuzun yeniden düzenlemenin yardımcı olmayacağı nadir istisnalardan biri olması çok iyi olabilir, ancak bunu ancak özenli yeniden düzenleme çabaları işe yaramadığında böyle beyan etmelisiniz. Deneyimli geliştiriciler bile kendi tasarımlarına alternatifleri görmekte zorluk çekebilirler. Bunu iyileştirmenin olası bir yolunu düşünemiyorsanız, deneyimli birinden ikinci bir görünüm vermesini isteyin.

Çoğu insan sizin CsvRecordbir değer sınıfı olduğunu varsayar . Bir tane yapmayı deneyin. Yapabiliyorsanız değişmez yapın. Birbirinize işaret eden iki nesneniz varsa, bunlardan birini kaldırın ve nasıl çalışacağını öğrenin. Sınıfları ve işlevleri bölecek yerleri arayın. Bir sınıfı bölmek için en iyi yer, dosyanın fiziksel düzeniyle her zaman eşleşmeyebilir. Sınıfların ebeveyn / çocuk ilişkisini tersine çevirmeyi deneyin. Belki csv dosyalarını okumak ve yazmak için ayrı bir sınıfa ihtiyacınız vardır. Belki dosya G / Ç ve üst katmanların arayüzünü işlemek için ayrı sınıflara ihtiyacınız vardır. Onarılamaz ilan etmeden önce denenecek çok şey var.

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.