Tam bir yeniden düzenleme için zaman olmadığında eski kod için testler yazmak mantıklı geliyor mu?


72

Ben genellikle kitabın tavsiyesi takip etmeye Legacy Cod ile Etkin Çalışma e . Bağımlılıkları bozuyorum, kodun bölümlerini @VisibleForTesting public staticyöntemlere ve kodu (veya en azından bir bölümünün) test edilebilir hale getirmek için yeni sınıflara taşıyorum. Ve değiştirdiğimde veya yeni işlevler eklerken hiçbir şeyi kırmadığımdan emin olmak için testler yazarım.

Bir meslektaşım bunu yapmamam gerektiğini söylüyor. Akıl yürütme:

  • Orijinal kod ilk etapta düzgün çalışmayabilir. Ve testler yazmak, gelecekteki düzeltmeleri ve değişiklikleri zorlaştırıyor çünkü devs de testleri anlamak ve değiştirmek zorunda kaldı.
  • Bazı mantıklara sahip GUI kodu ise (~ 12 satır, örneğin 2-3 if / else blok ise), kod başlamak için çok önemsiz olduğu için bir test sorun olmaz.
  • Benzer kötü kalıplar kod tabanının diğer kısımlarında da mevcut olabilir (ki henüz görmedim, daha çok yeniyim); hepsini büyük bir refactoring ile temizlemek daha kolay olacak. Mantığı çıkarmak, gelecekteki bu olasılığı zayıflatabilir.

Komple yeniden düzenleme için zamanımız yoksa test edilebilir parçaları çıkarmaktan ve test yazmaktan kaçınmalı mıyım? Göz önünde bulundurmam gereken herhangi bir dezavantaj var mı?


29
Görünüşe göre meslektaşın sadece mazeret veriyor, çünkü o böyle çalışmıyor. İnsanlar bazen kabul ettikleri şeyleri yapma biçimini değiştiremeyecek kadar inatçı oldukları için böyle davranırlar.
Doktor Brown,

3
bir böcek olarak sınıflandırılması gereken, onu bir özelliğe dönüştüren kodun diğer bölümleri tarafından güvenilebilir
cırcır ucube

1
Aklıma gelebilecek tek argüman, bir şeyi yanlış okuduğunuzda / yanlış okuduğunuzda yeniden canlandırmanın kendisinin yeni hatalar verebileceğidir. Bu nedenle, şu anda geliştirilmekte olan sürümdeki kalbimin içeriğini yeniden düzenlemek ve düzeltmekte özgürüm - ancak geçmiş sürümlerde yapılan düzeltmeler çok daha yüksek bir engelle karşı karşıya ve bu nedenle "yalnızca" kozmetik / yapısal temizlik yapıldıysa onaylanmayabilir. riskin potansiyel kazancı aştığı kabul edilir. Yerel kültürünüzü bilin - yalnızca bir ineğin değil - onun fikrini düşünün - ve başka bir şey yapmadan önce son derece güçlü sebeplere hazır olun.
keshlam

6
İlk nokta biraz komik - “Test etmeyin, sıkıcı olabilir.” Evet, evet? Öyleyse bunu bilmek güzel - ya bunu düzeltmek istiyoruz ya da kimsenin gerçek davranışı bazı tasarım özelliklerinin söylediği şekilde değiştirmesini istemiyoruz. Her iki durumda da, test (ve testleri otomatik bir sistemde çalıştırmak) faydalıdır.
Christopher Creutzig

3
Çok sık olmak üzere olan ve tüm hastalıkları tedavi edecek "büyük bir yeniden düzenleme" sıkıcı olarak düşündükleri şeyleri (test yazma) uzak geleceğe itmek isteyenler tarafından onaylanan bir efsanedir. Ve eğer gerçek olursa, çok büyük olmasına izin verdiğine pişmanlık duyacaklar!
Julia Hayward

Yanıtlar:


100

İşte kişisel bilimsel izlenim: bu üç nedenin hepsi yaygın ama yanlış bilişsel yanılsamalar gibi görünüyor.

  1. Elbette, mevcut kod yanlış olabilir. Aynı zamanda doğru olabilir. Başvuru bir bütün olarak sizin için değerli göründüğü için (aksi takdirde basitçe atarsınız), daha spesifik bilgilerin yokluğunda, ağırlıklı olarak doğru olduğunu varsaymalısınız. "Testleri yazmak işleri zorlaştırıyor çünkü genel olarak daha fazla kod var" basit ve çok yanlış bir tutumdur.
  2. Elbette yeniden değerlendirme, test etme ve iyileştirme çabalarını, en az çabayla en fazla katma değer verdikleri yerlerde harcayın. Değer biçimleme GUI alt yordamları çoğu zaman birinci öncelik değildir. Ancak bir şeyi sınamak değil, çünkü "basit" de çok yanlış bir tutumdur. Neredeyse tüm ciddi hatalar işlenir, çünkü insanlar bir şeyleri olduğundan daha iyi anladıklarını düşünüyorlardı.
  3. “Gelecekte her şeyi büyük bir baskınta yapacağız” güzel bir düşünce. Genellikle büyük baskın gelecekte sağlam bir şekilde dururken, şu anda hiçbir şey olmuyor. Ben, sıkı bir şekilde "yavaş ve istikrarlı bir yarışı kazandığını" mahkumiyetini alıyorum.

23
+1 "Neredeyse tüm ciddi hatalar işlendi, çünkü insanlar bir şeyleri olduğundan daha iyi anladığını sanıyorlardı."
Rem

Re noktası 1 - BDD ile , testler kendi kendini belgeliyor ...
Robbie Dee

2
@ Guillaume31'in işaret ettiği gibi, yazma testlerinin değerinin bir kısmı, kodun gerçekte nasıl çalıştığını - şartnameye / şartlara uygun olabilir veya olmayabilir. Ancak "yanlış" olan özellik olabilir: iş ihtiyaçları değişmiş olabilir ve kod yeni gereksinimleri yansıtıyor ancak teknik özellik böyle değil. Basitçe, kodun "yanlış" olduğunu varsaymak aşırı derecede basittir (bkz. Madde 1). Ve yine testler, kodun gerçekte ne yaptığını, birinin ne düşündüğünü / söylediğini söylemez (2. maddeye bakınız).
David,

Bir baskın yapsanız bile, kodu anlamanız gerekir. Sınamalar, yeniden ateşleme yapmamanız, ancak yeniden yazmanız bile beklenmedik bir davranışı yakalamanıza yardımcı olur (ve yeniden işlediğiniz takdirde yeniden yapılanmanın eski davranışları kırmadığından (veya yalnızca kırılmasını istediğiniz yerlerde) emin olmanıza yardımcı olur. İstediğiniz gibi dahil etmekten çekinmeyin.
Frank Hopkins,

50

Birkaç düşünce:

Eski kodu yeniden değerlendiriyorsanız, yazdığınız testlerden bazılarının ideal özelliklere aykırı olması önemli değil. Önemli olan programın mevcut davranışını test etmeleri . Yeniden düzenleme, kodu daha temiz hale getirmek için küçük izo işlevsel adımlar atmakla ilgilidir; yeniden düzenleme yaparken hata düzeltme ile uğraşmak istemezsiniz. Ayrıca, bariz bir hata görürseniz kaybolmaz. Bunun için her zaman bir regresyon testi yazabilir ve geçici olarak devre dışı bırakabilir ya da daha sonra bir süre içinde bir hata düzeltme görevi ekleyebilirsiniz. Bir seferde bir şey.

Saf GUI kodunun test edilmesi zor ve " Etkili Çalışmak ... " tarzı yeniden düzenlemeye uygun olmayabilir . Ancak, bu, GUI katmanında ilgisi olmayan davranışları ayıklamanız ve ayıklanan kodu test etmeniz gerektiği anlamına gelmez. Ve "12 satır, 2-3 ise / başka bir blok" önemsiz değildir. En azından biraz koşullu mantığa sahip tüm kodlar test edilmelidir.

Tecrübelerime göre, büyük yeniden düzenlemeler kolay değildir ve nadiren çalışırlar. Kendinize kesin ve küçük hedefler koymuyorsanız, hiç bitmeyen, saçları kesen bir elemeye geçme riski yoktur ve sonunda ayağınıza asla inemezsiniz. Değişim ne kadar büyük olursa, bir şeyleri kırma riskiniz o kadar artar ve nerede başarısız olduğunuzu bulmada o kadar fazla sorun yaşarsınız.

Özel küçük yeniden yapılandırmalarla işleri daha iyi hale getirmek, "gelecekteki olasılıkları baltalamak" değildir, bu onları mümkün kılar - uygulamanızın bulunduğu bataklık alanını sağlamlaştırır. Kesinlikle yapmalısın.


5
+1 "testler Eğer programın sınamak yazma geçerli davranışını "
David

17

Ayrıca şunu tekrarlayın: "Orijinal kod düzgün çalışmayabilir" - bu, etkiden endişe duymadan kodun davranışını değiştirdiğiniz anlamına gelmez. Diğer kod, bozuk davranışa ya da mevcut uygulamanın yan etkilerine bağlı olabilir. Var olan uygulamanın test kapsamı daha sonra yeniden yapılandırmayı kolaylaştırmak zorundadır, çünkü yanlışlıkla bir şeyi ne zaman kırdığınızı öğrenmenize yardımcı olur. İlk önce en önemli kısımları test etmelisiniz.


Acı gerçek. Müşterilerimiz doğruluk üzerinde tutarlılığı tercih ettiğinden, düzeltemeyeceğimiz son durumlarda ortaya çıkan birkaç belirgin hataya sahibiz. (Raporlama kodunun dikkate almadığı şeyleri sağlayan veri toplama kodundan kaynaklanmaktadır, bir alanı bir dizi alanda boş bırakmak gibi)
Izkata

14

Kilian'ın cevabı en önemli hususları kapsıyor, ancak 1. ve 3. noktalara genişletmek istiyorum.

Eğer bir geliştirici kodunu (refactor, extend, debug) kodunu değiştirmek istiyorsa, onu anlamak zorundadır. Yaptığı değişikliklerin tam olarak istediği davranışı (yeniden yapılanma durumunda hiçbir şey yok) ve başka hiçbir şeyi etkilememesini sağlamalıdır.

Eğer testler varsa, o zaman testleri de anlamak zorundadır. Aynı zamanda, testler ana kodu anlamasına yardım etmeli ve testler fonksiyonel koddan çok daha kolaydır (kötü testler olmadıkça). Testler, eski kodun davranışında neyin değiştiğini göstermeye yardımcı olur. Orijinal kod yanlış olsa ve test bu yanlış davranışı test etse bile, bu hala bir avantajdır.

Ancak, bu, testlerin bir şartname değil, önceden var olan davranışları test etme olarak belgelenmesini gerektirir.

3. noktadaki bazı düşünceler de: “büyük baskın” ın nadiren gerçekleştiği gerçeğine ek olarak, başka bir şey daha var: aslında kolay değil. Daha kolay olması için birkaç koşulun uygulanması gerekir:

  • Refaktörlü antipatternin kolayca bulunması gerekir. Tüm single'ların isimleri var XYZSingletonmı? Örnek alıcıları her zaman aranır getInstance()mı? Aşırı derin hiyerarşilerinizi nasıl buluyorsunuz? Tanrı nesnelerinizi nasıl ararsınız? Bunlar, kod ölçümleri analizini ve ardından ölçümleri manuel olarak incelemeyi gerektirir. Ya da sadece çalıştıkça üzerinize tökezlersiniz, yaptığınız gibi.
  • Yeniden yapılanmanın mekanik olması gerekiyor. Çoğu durumda, yeniden yapılanmanın zor kısmı, nasıl değiştirileceğini bilmek için mevcut kodu iyi anlamaktır. Yine Singletonlar: Eğer singleton bittiyse, gerekli bilgileri kullanıcılarına nasıl edersiniz? Bu genellikle yerel arama belgesini anlamak anlamına gelir, böylece bilgiyi nereden alacağınızı bilirsiniz. Şimdi daha kolay olan şey: uygulamanızdaki on tekili araştırmak, her birinin kullanımını anlamak (kod tabanının% 60'ını anlama ihtiyacı doğurur) ve bunları sökmek? Ya da halihazırda anladığınız kodu almak için (şu anda üzerinde çalışıyorsunuzdur) ve orada kullanılan singletonları sökmek? Yeniden yapılandırma işlemi o kadar mekanik değilse çevre koduyla ilgili hiçbir şey bilmeyecek kadar az bilgiye ihtiyaç duyar.
  • Yeniden yapılanmanın otomatikleştirilmesi gerekiyor. Bu biraz görüşe dayalı, ama işte gidiyor. Refactoring biraz eğlenceli ve tatmin edici. Bir çok yeniden düzenleme sıkıcı ve sıkıcıdır. Çalıştığınız kod parçasını daha iyi bir durumda bırakmak, daha ilginç şeylere geçmeden önce size hoş ve sıcak bir his verir. Bütün bir kod tabanını yeniden denemeye çalışmak, onu yazan aptal programcılara sinirli ve öfkeli olacak. Büyük bir baskın refactoring yapmak istiyorsanız, o zaman hayal kırıklığını en aza indirmek için büyük ölçüde otomatikleştirilmesi gerekir. Bu, bir şekilde, ilk iki noktanın bir sonucudur: Eğer kötü kodu bulmayı otomatikleştirmeyi (yani kolayca bulunan) otomatik hale getirebilir ve değiştirmeyi otomatik hale getirebilirseniz (yani mekanik).
  • Kademeli iyileşme daha iyi bir iş vakası oluşturur. Büyük baskın refactoring inanılmaz derecede rahatsız edici. Bir kod parçasını yeniden gözden geçirirseniz, üzerinde çalışmakta olan diğer insanlarla çatışmaları her zaman birleştiriyorsunuz çünkü yalnızca değiştirdikleri yöntemi beş kısma böldünüz. Makul büyüklükte bir kod parçasını yeniden düzenlediğinizde, birkaç kişiyle çatışma yaşarsınız (600 satırlık megafonu bölerken 1-2, tanrı nesnesini ayırırken 2-4, tektonu modülden çıkartırken 1-2) ), fakat yine de ana düzenlemeleriniz nedeniyle bu çatışmaları yaşamış olacaksınız. Kod tabanı genelinde bir yeniden yapılandırma işlemi yaptığınızda, herkesle çatışırsınız. Birkaç geliştiriciyi günlerce bağladığından bahsetmiyorum bile. Kademeli gelişim, her kod değişikliğinin biraz daha uzun sürmesine neden olur. Bu daha öngörülebilir yapar ve temizleme dışında hiçbir şey olmadığında gözle görülür bir zaman periyodu yoktur.

12

Bazı şirketlerde, geliştiricilerin doğrudan ek değer sağlamayan, örneğin yeni işlevsellik sağlamayan kodu geliştirme konusunda her zaman zamana izin verme konusunda isteksiz oldukları bir kültür var.

Muhtemelen burada dönüştürülen vaaz ediyorum, ama bu açıkça yanlış ekonomi. Temiz ve özlü kod müteakip geliştiricilere yarar sağlar. Sadece geri ödeme anında belli değil.

Şahsen izci prensibine abone oldum ama diğerleri (gördüğünüz gibi) yok.

Bununla birlikte, yazılım entropiden muzdariptir ve teknik borç yaratır. Önceki geliştiriciler zamanında kısa (ya da belki de sadece tembel ya da deneyimsiz) iyi tasarlanmış olanlara göre en uygun olmayan buggy çözümleri uygulamış olabilir. Bunları yeniden incelemek istenebilir gibi görünse de, (zaten kullanıcılara) çalışma kodunun ne olduğu konusunda yeni hatalar ortaya çıkma riskiyle karşı karşıya kalırsınız.

Bazı değişiklikler diğerlerinden daha düşük risklidir. Örneğin, çalıştığım yerde, en az etkiye sahip güvenli bir şekilde alt rutine güvenli bir şekilde yerleştirilebilecek çok sayıda kopya kod olma eğilimindedir.

Sonuç olarak, yeniden yapılanmayı ne kadar uzağa götüreceğinize dair bir yargılama çağrısı yapmanız gerekir, ancak önceden mevcut değilse, otomatikleştirilmiş testler eklenmesinde yadsınamaz bir değer vardır.


2
Prensipte tamamen katılıyorum, ancak birçok şirkette zaman ve paraya iniyor. Eğer "toparlanma" kısmı sadece birkaç dakika alırsa sorun olmaz, fakat toparlanma için tahminde bulunma bir kez büyüdüğünde (bazı büyük tanımlamalar için), siz, kodlayan kişinin bu kararı patronunuza devretmesi gerekir. proje Müdürü. Harcanan zamanın değerine karar vermek sizin yeriniz değil. Hata düzeltme X veya yeni özellik üzerinde çalışmak, Y'nin proje / şirket / müşteri için çok daha yüksek bir değere sahip olabilir.
ozz

2
Ayrıca, projenin 6 ay içinde hurdaya atılması veya şirketin zamanınızı daha fazla değerlendiriyor olması gibi daha büyük sorunların farkında olmayabilir (örneğin, daha önemli gördükleri bir şey yaparsınız ve başka birisi de yeniden düzenleme işini yapar). Yeniden yapılanma çalışmasının test üzerinde de etkisi olabilir. Büyük bir yeniden düzenleme, tam bir test regresyonunu tetikler mi? Şirketin bunu yapmak için kullanabileceği kaynakları var mı?
ozz

Evet, dokunduğunuzda, ana kod cerrahisinin iyi bir fikir olmasının ya da iyi bir fikir olmamasının çok sayıda nedeni vardır: diğer geliştirme öncelikleri, yazılımın ömrü, kaynak kaynağını test etme, geliştirici deneyimi, birleştirme, sürüm döngüsü, koda aşinalık baz, dokümantasyon, misyon kritiklik, şirket kültürü vs vs vs. Bu yargı çağrısıdır
Robbie Dee

4

Deneyimlerime göre, bir çeşit karakterizasyon testi iyi sonuç veriyor. Size nispeten hızlı bir şekilde geniş ama çok spesifik bir test kapsamı vermiyor, ancak GUI uygulamaları için uygulanması zor olabilir.

Daha sonra değiştirmek istediğiniz parçalar için birim testleri yazarım ve değişiklik yapmak istediğinizde bunu yapın, böylece zaman içinde birim test kapsamınızı arttırın.

Bu yaklaşım, değişiklikler sistemin diğer bölümlerini etkiliyorsa size iyi bir fikir verir ve gerekli değişiklikleri daha erken yapacak bir pozisyona girmenize izin verir.


3

Yanıt: "Orijinal kod düzgün çalışmayabilir":

Testler taşa yazılmamıştır. Değiştirilebilirler. Ve yanlış bir özellik için test yaptıysanız, testi daha doğru bir şekilde yeniden yazmak kolay olmalıdır. Sonuçta, yalnızca test edilen fonksiyonun beklenen sonucu değişmiş olmalıydı.


1
IMO, bireysel testler gerektiğini onlar test ediyoruz özellik ölüp gitmiş olan en az til, taşa yazılmış. Bunlar, mevcut sistemin davranışını doğrulayan şeydir ve değişiklik yapılmasının, söz konusu davranışa dayanan eski kodları ihlal etmeyeceğinden emin olmalarına yardımcı olur. Canlı bir özellik için testleri değiştirin; bu garantileri kaldırıyorsunuz.
cHao

3

İyi evet. Yazılım test mühendisi olarak cevap vermek. Öncelikle, zaten yaptığınız her şeyi test etmelisiniz. Çünkü eğer yapmazsan, çalışıp çalışmadığını bilmiyorsun. Bu bizim için bariz görünebilir ama ben onu farklı gören meslektaşlarım var. Projeniz asla teslim edilemeyecek küçük bir proje olsa bile, kullanıcıyı suratına bakmalı ve test ettiğinden çalıştığını bilmelisiniz.

Önemsiz olmayan kod her zaman hatalar içerir (üniversiteden bir kişiden alıntı yapmak; ve eğer hiçbir hata yoksa önemsizdir) ve işimiz müşteri onlardan önce onları bulmaktır. Eski kodun eski hataları var. Orijinal kod olması gerektiği gibi çalışmıyorsa, bilmek istersiniz, inan bana. Onları biliyorsanız, onları bulmaktan korkmayın, sürüm notları bunun için.

Doğru hatırlıyorsam, Refactoring kitabı zaten sürekli test etmeyi söylüyor. Bu, sürecin bir parçası.


3

Otomatik test kapsamı yapın.

Hem kendinize ait, hem de müşterileriniz ve patronlarınız tarafından arzulu düşüncelere dikkat edin. Değişikliklerimin ilk kez doğru olacağına inanmayı çok isterdim ve yalnızca bir kez test etmek zorunda kalacağım gibi, bu tür bir düşünceyi Nijeryalı aldatmaca e-postalarına davrandığım gibi ele almayı öğrendim. Eh, çoğunlukla; Hiç bir aldatmaca e-posta için gittim ama son zamanlarda (bağırdı zaman) en iyi uygulamaları kullanmamaya verdim. Acı ve sürükleyici bir deneyim oldu. Bir daha asla!

Freefall web comicicinden favori bir alıntı yaptım: "Denetçinin teknik detaylar hakkında sadece kaba bir fikre sahip olduğu karmaşık bir alanda çalıştınız mı?" sorusunu her soru sormadan takip et. "

Yatırım yapma sürenizi sınırlamak muhtemelen uygun olacaktır.


1

Şu anda test edilmeyen büyük miktarda eski kodla uğraşıyorsanız, gelecekte varsayımsal büyük bir yeniden yazma beklemek yerine şimdi test kapsamı almak doğru harekettir. Ünite testleri yazarak başlamak değil.

Otomatik testler olmadan, kodda herhangi bir değişiklik yaptıktan sonra, uygulamanın çalıştığından emin olmak için uygulamanın testini sonlandırmak için el ile bir şeyler yapmanız gerekir. Bunun yerine yüksek seviye entegrasyon testleri yazarak başlayın. Uygulamanız dosyaları okursa, doğrularsa, verileri bir şekilde işler ve hepsini yakalayan testleri istediğiniz sonuçları görüntüler.

İdeal olarak, manuel bir test planından elde edeceğiniz veriler olacaktır veya kullanılacak gerçek üretim verilerinden bir örnek alabileceksiniz. Değilse, uygulama üretimde olduğundan, çoğu durumda olması gerekeni yapıyor, bu yüzden tüm yüksek noktalara vuracak ve çıktının şu an için doğru olduğunu varsayacak veriyi hazırla. Küçük bir işlev almaktan daha kötü bir şey değildir, adının ne yaptığını veya herhangi bir yorum yapması gerektiğini önerdiğini varsayarak ve doğru çalıştığını varsayarak sınamalar yazmak varsaymak değildir.

IntegrationTestCase1()
{
    var input = ReadDataFile("path\to\test\data\case1in.ext");
    bool validInput = ValidateData(input);
    Assert.IsTrue(validInput);

    var processedData = ProcessData(input);
    Assert.AreEqual(0, processedData.Errors.Count);

    bool writeError = WriteFile(processedData, "temp\file.ext");
    Assert.IsFalse(writeError);

    bool filesAreEqual = CompareFiles("temp\file.ext", "path\to\test\data\case1out.ext");
    Assert.IsTrue(filesAreEqual);
}

Uygulamaların normal çalışmasını yakalamak için yazılan bu yüksek seviye testlerden yeterince geçtikten sonra ve en sık karşılaşılan hata durumları, koddaki hataları yapmaktan başka bir şey yapmadan hataları yakalamak için klavyeye vurmak için harcayacağınız süredir. Yapmanız gerekeceğini, gelecekteki refactoring (veya büyük bir yeniden yazma) sürecini çok daha kolay hale getirecek şekilde önemli ölçüde azaltacağını düşündünüz.

Birim test kapsamını genişletebildiğiniz için, entegrasyon testlerinin çoğunu azaltabilir veya hatta emekli olabilirsiniz. Uygulamanızın dosyaları okuyor / yazıyorsa veya bir DB'ye erişiyorsa, bu bölümleri yalıtımlı olarak sınama ve bunları alay etme veya sınamalarınızın dosyadan / veritabanından okunan veri yapıları oluşturarak başlatılması başlangıç ​​için açık bir yerdir. Aslında bu test altyapısının oluşturulması, bir dizi hızlı ve kirli test yazmaktan çok daha uzun sürer; ve 30 dakika harcamak yerine 2 dakikalık bir entegrasyon testi seti çalıştırdığınızda, entegrasyon testlerinin kapsadığı şeyin bir kısmını manuel olarak test ederek zaten büyük bir kazanç elde edersiniz.

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.