TDD ve Yeniden Düzenleme ile ilgili Zorluklar (Veya - Neden Olması Gerekenden Daha Acı Verir?)


20

Kendime TDD yaklaşımını kullanmayı öğretmek istedim ve bir süredir üzerinde çalışmak istediğim bir projem vardı. Büyük bir proje değildi, bu yüzden TDD için iyi bir aday olacağını düşündüm. Ancak, bir şey ters gitti gibi hissediyorum. Bir örnek vereyim:

Üst düzeyde projem, Microsoft OneNote için Projeleri daha kolay izlememe ve yönetmeme olanak tanıyan bir eklentidir. Şimdi, bunun için iş mantığını OneNote'tan ayrıştırılmış olarak tutmak istedim, çünkü kendi özel depolama alanımı oluşturmaya karar verdim ve bir gün arka uç.

İlk olarak, ilk özelliğimin ne yapmasını istediğimi açıklamak için basit bir düz kelime kabul testiyle başladım. Böyle bir şeye benziyor (kısalık için onu batırıyor):

  1. Kullanıcı proje oluştur'u tıklar
  2. Proje başlığında kullanıcı türleri
  3. Projenin doğru oluşturulduğunu doğrulayın

Kullanıcı arayüzü öğelerini atlama ve bazı aracı planlama İlk birim testime geliyorum:

[TestMethod]
public void CreateProject_BasicParameters_ProjectIsValid()
{
    var testController = new Controller();
    Project newProject = testController(A.Dummy<String>());
    Assert.IsNotNull(newProject);
}

Çok uzak çok iyi. Kırmızı, yeşil, refactor, vb. Tamam. Burada bazı adımları keserek bununla bitiyorum.

[TestMethod]
public void CreateProject_BasicParameters_ProjectMatchesExpected()
{
    var fakeDataStore = A.Fake<IDataStore>();
    var testController = new Controller(fakeDataStore);
    String expectedTitle = fixture.Create<String>("Title");
    Project newProject = testController(expectedTitle);

    Assert.AreEqual(expectedTitle, newProject.Title);
}

Bu noktada hala iyi hissediyorum. Henüz somut bir veri mağazam yok, ancak arayüzün nasıl görüneceğini tahmin ettiğimi oluşturdum.

Bu yazı yeterince uzun sürdüğü için burada birkaç adım atlayacağım, ancak benzer işlemleri takip ettim ve sonunda veri deposum için bu sınava giriyorum:

[TestMethod]
public void SaveNewProject_BasicParameters_RequestsNewPage()
{
    /* snip init code */
    testDataStore.SaveNewProject(A.Dummy<IProject>());
    A.CallTo(() => oneNoteInterop.SavePage()).MustHaveHappened();
}

Ben uygulamaya çalışana kadar bu iyi oldu:

public String SaveNewProject(IProject project)
{
    Page projectPage = oneNoteInterop.CreatePage(...);
}

Ve "..." nin olduğu yerde sorun var. Şimdi bu noktada CreatePage bir bölüm kimliği gerektirdiğini anlıyorum. Kontrolör seviyesinde düşünürken bunu fark etmedim çünkü sadece kontrolörle ilgili bitleri test etmekle ilgilendim. Ancak, burada tüm yol şimdi kullanıcıdan projeyi saklamak için bir konum sormak zorunda olduğumu fark ediyorum. Şimdi veri deposuna bir konum kimliği eklemeliyim, sonra projeye bir tane eklemeliyim, sonra denetleyiciye bir tane eklemeliyim ve bunları tüm bunlar için yazılmış olan TÜM testlere eklemeliyim. Çok hızlı bir şekilde sıkıcı hale geldi ve TDD süreci sırasında tasarlanmasına izin vermek yerine tasarımı önceden çizmiş olsaydım bu kadar hızlı yakalayabileceğimi hissediyorum.

Bu süreçte yanlış bir şey yaptıysam birisi bana açıklayabilir mi? Yine de bu tür bir yeniden düzenleme önlenebilir mi? Yoksa bu yaygın mı? Yaygın ise, daha acısız hale getirmenin herhangi bir yolu var mı?

Hepinize teşekkürler!


Bu konuyu şu tartışma forumuna gönderirseniz çok anlayışlı yorumlar alırsınız: özellikle TDD konuları için olan groups.google.com/forum/#!forum/… .
Chuck Krutsinger

1
Tüm testlerinize bir şeyler eklemeniz gerekiyorsa, testleriniz iyi yazılmış gibi görünmektedir. Testlerinizi yeniden düzenlemeli ve makul bir fikstür kullanmayı düşünmelisiniz.
Dave Hillier

Yanıtlar:


19

TDD (haklı olarak) yazılımınızı tasarlamanın ve büyütmenin bir yolu olarak lanse edilirken, tasarım ve mimariyi önceden düşünmek hala iyi bir fikirdir. IMO, "tasarımın vaktinden önce çizilmesi" adil bir oyundur. Bununla birlikte, bu genellikle TDD aracılığıyla yönlendireceğiniz tasarım kararlarından daha yüksek bir seviyede olacaktır.

İşler değiştiğinde, genellikle testleri güncellemeniz gerekeceği de doğrudur. Bunu tamamen ortadan kaldırmanın bir yolu yoktur, ancak testlerinizi daha az kırılgan hale getirmek ve ağrıyı en aza indirmek için yapabileceğiniz bazı şeyler vardır.

  1. Uygulama ayrıntılarını mümkün olduğunca testlerinizin dışında tutun. Bu, yalnızca herkese açık yöntemlerle test etmek ve mümkünse etkileşime dayalı doğrulamaya göre devlet temelli bir tercih anlamına gelir . Başka bir deyişle, oraya ulaşmak için gereken adımlardan ziyade bir şeyin sonucunu test ederseniz, testleriniz daha az kırılgan olmalıdır.

  2. Üretim kodunda yaptığınız gibi test kodunuzdaki çoğaltmayı en aza indirin. Bu gönderi iyi bir referans. IDÖrneğinizde, kurucuyu birkaç farklı testte doğrudan çağırdığınız için özelliği kurucunuza eklemenin acı verici olduğu anlaşılıyor . Bunun yerine, nesnenin oluşturulmasını bir yönteme çıkarmayı veya bir test başlatma yöntemindeki her test için bir kez başlatmayı deneyin.


Devlet temelli ve etkileşim temelli değerlerini okudum ve çoğu zaman anlıyorum. Bununla birlikte, test için özellikleri açıkça göstermeden her durumda nasıl mümkün olduğunu görmüyorum. Yukarıdaki örneğimi ele alalım. Nasıl "MustHaveBeenCalled" için bir iddia kullanmadan veri deposunun gerçekten çağrıldığını kontrol emin değilim. 2. noktaya gelince, kesinlikle haklısın. Tüm düzenlemelerden sonra bunu yaptım, ancak yaklaşımımın genel olarak kabul edilen TDD uygulamalarıyla tutarlı olduğundan emin olmak istedim. Teşekkürler!
Landon

@Landon Etkileşim testinin daha uygun olduğu durumlar vardır. Örneğin, bir veritabanına veya web hizmetine çağrı yapıldığını doğrulama. Temel olarak, testinizi özellikle harici bir hizmetten izole etmeniz gerektiğinde.
jhewlett

@ Ima bir "ikna klasikçi", bu yüzden entegrasyon tabanlı test konusunda çok deneyimli değilim ... Ama "MustHaveBeenCalled" için bir iddiada olmanıza gerek yok. Bir eklemeyi test ediyorsanız, eklenip eklenmediğini görmek için bir sorgu kullanabilirsiniz. PS: Ben veritabanı katmanı dışında her şeyi test ederken performans dikkate nedeniyle saplamalar kullanın.
Hbas

@jhewlett Bu da benim vardığım sonuç. Teşekkürler!
Landon

@Hbas Sorgulanacak veritabanı yok. Ben olsaydı gitmek için en basit yol olacağını kabul ediyorum, ama bunu bir OneNote not defterine ekliyorum. Bunun yerine yapabileceğim en iyi şey, sayfayı çekmeye çalışmak için birlikte çalışmam yardımcı sınıfına bir Get yöntemi eklemektir. Bunu yapmak için testi yazabilirdim, ama aynı anda iki şeyi test edeceğimi hissettim: Bunu kurtardım mı? ve Yardımcı sınıfım sayfaları doğru şekilde alıyor mu? Her ne kadar, sanırım bir noktada testleriniz başka bir yerde test edilen diğer koda dayanabilir. Teşekkürler!
Landon

10

... TDD süreçleri sırasında tasarlanmasına izin vermek yerine, tasarımı önceden çizmiş olsaydım bu kadar çabuk yakalamış gibi hissediyorum ...

Belki, belki değil

Bir yandan, TDD gayet iyi çalıştı, işlevselliği geliştirirken size otomatik testler verdi ve arayüzü değiştirmek zorunda kaldığınızda hemen kırıldı.

Öte yandan, belki de daha düşük düzeyli bir özellik (CreateProject) yerine yüksek düzeyli özellik (SaveProject) ile başlamış olsaydınız, eksik parametreleri daha erken fark etmiş olursunuz.

Sonra tekrar, belki olmazdı. Tekrarlanamayan bir deney.

Ancak bir dahaki sefere bir ders arıyorsanız: en baştan başlayın. Ve tasarımı ilk önce istediğiniz kadar düşünün.


0

https://frontendmasters.com/courses/angularjs-and-code-testability/ Yaklaşık 2:22:00 - bitiş (yaklaşık 1 saat). Maalesef video ücretsiz değil, ancak bu kadar iyi açıklayan ücretsiz bir video bulamadım.

Test edilebilir kod yazmanın en iyi sunumlarından biri bu derstir. Bu bir AngularJS sınıfıdır, ancak test kısmı java koduyla ilgilidir, çünkü öncelikle konuştuğu şeyin dil ile ilgisi yoktur ve ilk etapta iyi test edilebilir kod yazmakla ilgisi yoktur.

Sihir, kod testleri yazmak yerine test edilebilir kod yazmaktır. Bu, kullanıcı gibi davranan kod yazmakla ilgili değildir.

Ayrıca, spesifikasyonu test beyanı şeklinde yazmak için biraz zaman harcıyor.

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.