Yeniden düzenleme - tüm testler geçtiği sürece kodu yeniden yazmak uygun mudur?


9

Kısa bir süre önce RailsConf 2014'ten "Tüm Küçük Şeyler" izledim. Bu konuşma sırasında Sandi Metz, büyük bir iç içe if ifadesi içeren bir işlevi yeniden düzenler:

def tick
    if @name != 'Aged Brie' && @name != 'Backstage passes to a TAFKAL80ETC concert'
        if @quality > 0
            if @name != 'Sulfuras, Hand of Ragnaros'
                @quality -= 1
            end
        end
    else
        ...
    end
    ...
end

İlk adım, işlevi birkaç küçük işleve ayırmaktır:

def tick
    case name
    when 'Aged Brie'
        return brie_tick
    ...
    end
end

def brie_tick
    @days_remaining -= 1
    return if quality >= 50

    @quality += 1
    @quality += 1 if @days_remaining <= 0
end

İlginç bulduğum şey, bu daha küçük fonksiyonların yazılma biçimiydi. brie_tickörneğin, orijinal tickişlevin ilgili bölümleri çıkarılarak değil , test_brie_*birim testlerine atıfla sıfırdan yazılmıştır . Tüm bu birim testleri geçtikten sonra brie_tickyapıldı. Tüm küçük fonksiyonlar yapıldıktan sonra, orijinal monolitik tickfonksiyon silindi.

Ne yazık ki, sunumcu bu yaklaşımın dört *_tickfonksiyondan üçünün yanlış olmasına (ve diğerinin boş olduğuna!) Neden olmadığından habersiz görünüyordu . *_tickİşlevlerin davranışının orijinal tickişlevin davranışından farklı olduğu uç durumlar vardır . Örneğin, @days_remaining <= 0içinde brie_tickolmalıdır < 0- bu yüzden brie_tickile çağrıldığında düzgün çalışmaz days_remaining == 1ve quality < 50.

Burada neyin yanlış gitti? Bu bir test başarısızlığı mıdır - çünkü bu belirli uç durumlar için test yapılmamıştır? Ya da yeniden düzenleme başarısızlığı - çünkü kod sıfırdan yeniden yazmak yerine adım adım dönüştürülmüş olmalı?


2
Soruyu aldığımdan emin değilim. Elbette kodu yeniden yazmakta sorun yok. Özellikle " sadece kodu yeniden yazmanız uygun mu ?" Eğer soruyorsun "tamam içine düşünce çok koymadan kodunu yeniden yazmak mı," Eğer cevabım için OK değil, tıpkı hayır yazma o şekilde kod.
John Wu

Bu genellikle, çoğunlukla başarılı kullanım örneklerini test etmeye odaklanan ve hata kullanım vakalarını veya alt kullanım vakalarını kapsayan çok az (veya hiç değil) test planları nedeniyle olur. Yani esas olarak bir kapsama kaçağı. Bir test sızıntısı.
Laiv

@JohnWu - Yeniden düzenleme işleminin genellikle kodu yeniden yazmak yerine (hatta sıfırdan tekrar yazmak demek yerine) kaynak koduna ("özü yöntemi" vb.) Bir dizi küçük dönüşüm olarak yapıldığı izlenimi altındaydım. bağlı sunumda olduğu gibi mevcut koda bakıldığında).
user200783

@JohnWu - Sıfırdan yeniden yazma kabul edilebilir bir yeniden düzenleme tekniği midir? Değilse, yeniden düzenleme konusunda böyle saygın bir sunumun bu yaklaşımı benimsemesi hayal kırıklığı yaratıyor. OTOH kabul edilebilirse, eksik testlerde istenmeyen davranış değişiklikleri suçlanabilir - ancak testlerin olası tüm uç durumları kapsadığından emin olmanın herhangi bir yolu var mı?
user200783

@ User200783 Bu daha büyük bir soru, değil mi (testlerimin kapsamlı olmasını nasıl sağlarım?) Pragmatik olarak, muhtemelen herhangi bir değişiklik yapmadan önce bir kod kapsamı raporu çalıştırır ve kod olmayan tüm alanları dikkatlice incelerim egzersiz ekibinin, mantığı yeniden yazarken geliştirme ekibinin bunlara dikkat etmesini sağlamak.
John Wu

Yanıtlar:


11

Bu bir test başarısızlığı mıdır - çünkü bu belirli uç durumlar için test yapılmamıştır? Ya da yeniden düzenleme başarısızlığı - çünkü kod sıfırdan yeniden yazmak yerine adım adım dönüştürülmüş olmalı?

Her ikisi de. Fowlers orijinal kitabından sadece standart adımları kullanarak yeniden düzenleme kesinlikle bir yeniden yazma yapmaktan daha az hataya açıktır, bu nedenle sadece bu tür bebek adımlarını kullanmak genellikle tercih edilir. Her kenar durumu için birim testi olmasa bile ve ortam otomatik yeniden düzenleme yapmasa bile, "açıklayan değişkeni tanıt" veya "çıkarma işlevi" gibi tek bir kod değişikliğinin, bir fonksiyonun tam olarak yeniden yazılması yerine mevcut kod.

Bununla birlikte, bazen, bir kod bölümünü yeniden yazmak, ihtiyacınız olan veya yapmak istediğiniz şeydir. Ve bu durumda, daha iyi testlere ihtiyacınız var.

Bir yeniden düzenleme aracı kullanırken bile, kodu değiştirdiğinizde, daha küçük veya daha büyük adımlar uygulanmasına bakılmaksızın, her zaman belirli bir hata verme riski bulunduğunu unutmayın. Bu yüzden yeniden düzenleme her zaman testlere ihtiyaç duyar. Ayrıca testlerin sadece hata olasılığını azaltabileceğini, ancak bunların yokluğunu asla kanıtlayamayacağını unutmayın - yine de koda ve şube kapsamına bakmak gibi teknikler kullanmak size yüksek bir güven düzeyi verebilir ve bir kod bölümünün yeniden yazılması durumunda, genellikle bu tür teknikleri uygulamaya değer.


1
Teşekkürler, bu mantıklı. Bu nedenle, davranıştaki istenmeyen değişikliklerin nihai çözümü kapsamlı testlere sahip olmaksa, testlerin tüm olası son durumları kapsadığından emin olmanın herhangi bir yolu var mı? Örneğin brie_tick, sorunlu @days_remaining == 1vakayı hiçbir zaman test etmemekle birlikte % 100 kapsama sahip olmak , örneğin ve ile @days_remainingayarlanmış testlerle mümkün olabilir . 10-10
user200783

2
Testlerin tüm olası uç durumlarını kapsadığından kesinlikle emin olamazsınız, çünkü tüm olası girişlerle test etmek mümkün değildir. Ancak testlerde daha fazla güven kazanmanın birçok yolu vardır. Testlerin etkinliğini test etmenin bir yolu olan mutasyon testine bakabilirsiniz .
bdsl

1
Bu durumda, kaçırılan dallar testleri geliştirirken bir kod kapsamı aracıyla yakalanmış olabilir.
cbojar

2

Burada neyin yanlış gitti? Bu bir test başarısızlığı mıdır - çünkü bu belirli uç durumlar için test yapılmamıştır? Ya da yeniden düzenleme başarısızlığı - çünkü kod sıfırdan yeniden yazmak yerine adım adım dönüştürülmüş olmalı?

Eski kodla çalışmak konusunda gerçekten zor olan şeylerden biri: mevcut davranışı tam olarak anlamak.

Tüm davranışları sınırlayan testlerin olmadığı eski kod , vahşi doğada yaygın bir kalıptır. Bu da sizi bir tahminde bırakıyor: bu, kısıtlanmamış davranışların serbest değişkenler olduğu anlamına mı geliyor? ya da eksik şartlar?

Konuşmadan :

Şimdi bu yeniden düzenleme tanımına göre gerçek yeniden düzenleme. Bu kodu yeniden düzenleyeceğim. Davranışını değiştirmeden düzenlemesini değiştireceğim.

Bu daha muhafazakar yaklaşımdır; eğer gereksinimler yeterince belirtilmemişse, testler mevcut mantığın tamamını yakalamıyorsa, nasıl ilerlediğiniz konusunda çok dikkatli olmalısınız.

Kesin olarak, eğer testler sistemin davranışını yeterince tanımlamıyorsa, "test başarısızlığı" olduğunu iddia edebilirsiniz. Bence bu adil - ama aslında kullanışlı değil; Bu vahşi doğada yaygın bir sorundur .

Ya da yeniden düzenleme başarısızlığı - çünkü kod sıfırdan yeniden yazmak yerine adım adım dönüştürülmüş olmalı?

Sorun değil oldukça dönüşümler adım-adım olmalıydı ki; daha ziyade, yeniden düzenleme aracı (kılavuzlu otomasyon yerine insan klavyesi operatörü?) seçimi, daha yüksek hata oranı nedeniyle test kapsamıyla iyi hizalanmamıştır.

Bu ele olabilir ya da daha yüksek bir güvenilirlik üstlenmeden araçları kullanarak ya da sistem üzerinde kısıtlamalar geliştirmek için testler daha geniş bir pil sokulmasıyla.

Bu yüzden bağlantınızın kötü seçilmiş olduğunu düşünüyorum; ANDdeğil OR.


2

Yeniden düzenleme kodunuzun harici olarak görünür davranışını değiştirmemelidir. Amaç bu.

Birim testleriniz başarısız olursa, bu davranışı değiştirdiğinizi gösterir. Ancak birim testlerini geçmek asla amaç değildir. Hedefinize ulaşmak için az çok yardımcı olur. Yeniden düzenleme harici olarak görünen davranışı değiştirirse ve tüm birim testleri başarılı olursa yeniden düzenleme başarısız olur.

Bu durumda çalışma ünitesi testleri size sadece yanlış başarı hissi verir. Ama neyin yanlış gitti? İki şey: Yeniden düzenleme dikkatsizdi ve birim testleri çok iyi değildi.


1

Eğer “doğru” yu “testler geçiyor” olarak tanımlarsanız , tanım gereği test edilmemiş davranışı değiştirmek yanlış olmaz.

Belirli bir kenar davranışı ise edilmelidir tanımlanmış, daha sonra 's Tamam ne olacağı umurumda değil, ona bir test eklemek, değilse. Gerçekten bilgiç biriyseniz, truebu kenar durumunda ne zaman davranışın ne olduğunu umursamadığınızı belgelemek için bir test yazabilirsiniz .

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.