TDD testleri, aynı zamanda teste ihtiyaç duyan yeni işlevsellik gösterdiğinde ne yapmalı?


13

Bir test yazdığınızda ve test geçişini yapmanız gereken noktaya geldiğinizde ne yaparsınız ve kendi işlevine ayrılması gereken ek bir işlevsellik parçasına ihtiyacınız olduğunu fark edersiniz? Bu yeni işlevin de test edilmesi gerekiyor, ancak TDD döngüsü Bir testi başarısız yap, onu geç ve sonra yeniden düzenleme yap diyor. Test geçişimi yapmaya çalıştığım adımdaysam, gitmem ve uygulamam gereken yeni işlevselliği test etmek için başka bir başarısız test başlatmamam gerekiyor.

Örneğin, WillCollideWith ( LineSegment ) işlevi olan bir nokta sınıfı yazıyorum :

public class Point {
    // Point data and constructor ...

    public bool CollidesWithLine(LineSegment lineSegment) {
        Vector PointEndOfMovement = new Vector(Position.X + Velocity.X,
                                               Position.Y + Velocity.Y);
        LineSegment pointPath = new LineSegment(Position, PointEndOfMovement);
        if (lineSegment.Intersects(pointPath)) return true;
        return false;
    }
}

LineSegment.Intersects ( LineSegment ) işlevine ihtiyacım olacağını fark ettiğimde CollidesWithLine için bir test yazıyordum . Ancak, bu yeni işlevselliği oluşturmak için test döngümde ne yaptığımı durdurmalı mıyım? Bu "Kırmızı, Yeşil, Refaktör" ilkesini çiğniyor gibi görünüyor.

Sadece lineSegments Çarpışmaları CollidesWithLine işlevinin içinde kesiştiğini ve kodu çalıştıktan sonra yeniden düzenlediğini algılayan bir kod yazmalı mıyım ? LineSegment'ten verilere erişebildiğim için bu durumda işe yarar , ama bu tür verilerin özel olduğu durumlarda ne olur?

Yanıtlar:


14

Sadece testinizi ve son kodunuzu yorumlayın (veya bir stash içine koyun), böylece aslında saati döngünün başlangıcına geri döndürdünüz. Sonra LineSegment.Intersects(LineSegment)test / kod / refactor ile başlayın . Bu bittiğinde, önceki testinizi / kodunuzu açın (veya saklamaktan çekin) ve döngüye devam edin.


Bu sadece onu görmezden gelip daha sonra geri gelmenin nasıl bir farkı var?
Joshua Harris

1
sadece küçük ayrıntılar: raporlarda fazladan "beni yoksay" testi yoktur ve stash kullanırsanız, kod 'temiz' durumdan ayırt edilemez.
Javier

Zulası nedir? sürüm kontrolü gibi mi?
Joshua Harris

1
bazı VCS bunu bir özellik olarak uygular (en azından Git ve Fosil). Bir değişikliği kaldırmanıza, ancak bir süre sonra yeniden başvurmak üzere kaydetmenize olanak tanır. Elle yapmak zor değil, sadece bir fark kaydedin ve son duruma geri dönün. Daha sonra farkı yeniden uygular ve devam edersiniz.
Javier

6

TDD döngüsünde:

"Test geçişini yap" aşamasında, test geçişini yapacak en basit uygulamayı yazmanız gerekir . Test geçişinizi yapmak için, eksik mantığı işlemek için yeni bir ortak çalışan oluşturmaya karar verdiniz, çünkü test geçişinizi yapmak için puan sınıfınıza koymak çok fazla işti. Sorun burada yatıyor. Sanırım geçmek için yaptığınız test çok büyük bir adımdı . Bu yüzden sorunun testinizin kendisinde yattığını düşünüyorum, bu testi silmeli / yorumlamalısınız ve LineSegment.Intersects (LineSegment) bölümünü tanıtmadan bir bebek adımı atmanızı sağlayacak daha basit bir test bulmalısınız. Bu testten geçtiğinizde refactor yapabilirsinizBu yeni mantığı bir LineSegment.Intersects (LineSegment) yöntemine taşıyarak kodunuzu (Burada SRP ilkesini uygulayacaksınız). Herhangi bir davranışı değiştirmeyeceğiniz, ancak bazı kodları değiştirdiğiniz için testleriniz yine de geçecek.

Mevcut tasarım çözümünüz hakkında

Ama bana göre, burada daha derin bir tasarım sorununuz var, Tek Sorumluluk İlkesini ihlal ediyorsunuz . Bir Noktanın rolü .... bir nokta olmaktır, hepsi bu. Bir nokta olmanın akıllılığı yoktur, sadece ve x ve y değeridir. Noktalar değer tipleridir . Segmentler için de aynı şey geçerlidir, segmentler iki noktadan oluşan değer türleridir. Örneğin noktalarının konumlarına göre uzunluklarını hesaplamak için biraz "akıllılık" içerebilirler. Ama bu kadar.

Şimdi bir noktanın ve bir parçanın çarpışıp çarpışmadığına karar vermek tek başına bir sorumluluktur. Ve kesinlikle bir nokta veya segmentin kendi başına halletmesi için çok fazla iştir. Point sınıfına ait olamaz çünkü aksi halde Puanlar Segmentleri bilir. Ve Segmentlere ait olamaz çünkü Segmentler zaten segment içindeki noktalara dikkat etme ve belki de segmentin uzunluğunu hesaplama sorumluluğuna sahiptir.

Yani bu sorumluluk, örneğin "PointSegmentCollisionDetector" gibi başka bir sınıfa ait olmalıdır:

bool AreInCollision (Nokta p, Bölümler)

Ve bu Puan ve Segmentlerden ayrı olarak test edeceğiniz bir şey.

Bu tasarımın güzel yanı, çarpışma dedektörünüzün farklı şekilde uygulanabilmesidir. Bu nedenle, örneğin çarpışma algılama yönteminizi çalışma zamanında değiştirerek oyun motorunuzu karşılaştırmak (bir oyun yazdığınızı varsayıyorum): p. Ya da çalışma sırasında farklı çarpışma tespit stratejileri arasında görsel kontrol / deneyler yapmak.

Şu anda, bu mantığı nokta sınıfınıza koyarak, şeyleri kilitliyor ve Point sınıfına çok fazla sorumluluk yüklüyorsunuz.

Umarım mantıklıdır,


Benim bir değişimin çok büyük teste çalıştığını haklısın ve haklı bir çarpışma sınıfa dikkat ayrılarak hakkında düşünmek, ama bu beni bana yardım edebileceğini bu tamamen yeni bir soru sormak yapar: Meli I sadece benzer olduğunda bir arayüz kullanın? .
Joshua Harris

2

Bir TDD tarzında yapılacak en kolay şey, LineSegment için bir arabirim ayıklamak ve arabirimde almak için yöntem parametrenizi değiştirmek olacaktır. Daha sonra giriş hattı segmentini alay edebilir ve Kesişim yöntemini bağımsız olarak kodlayabilir / test edebilirsiniz.


1
Bunun en çok duyduğum TDD yöntemi olduğunu biliyorum, ancak bir ILineSegment mantıklı değil. Harici bir kaynağı veya birçok şekilde gelebilecek bir şeyi arayüzlemek için bir şeydir, ancak bir işlevsellikten herhangi birini bir çizgi segmentinden başka bir şeye iliştirmem için bir neden göremiyorum.
Joshua Harris

0

JUnit4 @Ignoreile ertelemek istediğiniz testler için ek açıklamayı kullanabilirsiniz .

Ertelemek istediğiniz her yönteme Ek Açıklama ekleyin ve gerekli işlevler için testler yazmaya devam edin. Eski test vakalarını daha sonra yeniden düzenlemek için geri dönün.

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.