Birim testlerinde döngüsel bağımlılıklarla mücadele


24

TDD'yi pratik etmeye çalışıyorum, Bit Vector gibi basit bir yazılım geliştirerek. Swift kullanıyorum ama bu dille ilgili bir soru.

Benim BitVectorbir olduğunu structdepolayan tek UInt64ve hediyeler bir koleksiyon gibi davranın sağlayan üzerine bir API. Detaylar çok önemli değil, ama oldukça basit. Yüksek 57 bit depolama bitleri ve düşük 6 bit "sayım bitleri" dir, bu da size depolama bitlerinin kaç tanesinin gerçek bir değeri sakladığını gösterir.

Şimdiye kadar çok basit yeteneklerim var:

  1. Boş bit vektörleri oluşturan bir başlatıcı
  2. countTür bir özellikInt
  3. Bir tür isEmptyözelliğiBool
  4. Bir eşitlik operatörü ( ==). Not: Bu, Object.equals()Java'daki gibi bir referans eşitliği operatörü değil, Java'daki bir değer eşitliği operatörüdür ==.

Bir sürü döngüsel bağımlılıkla karşılaşıyorum:

  1. Başlatıcımı test eden ünite testi, yeni yapılanmayı doğrulamak zorunda BitVector. Bunu 3 yoldan biriyle yapabilir:

    1. Kontrol bv.count == 0
    2. Kontrol bv.isEmpty == true
    3. Şunu kontrol et bv == knownEmptyBitVector

    Yöntem 1 dayanıyor count, yöntem 2 dayanıyor isEmpty(kendine güveniyor count, bu yüzden onu kullanmanın bir anlamı yok), yöntem 3 dayanıyor ==. Her halükarda, başlatıcımı yalıtılmış olarak test edemiyorum.

  2. Testin count, başlatıcılarımı kaçınılmaz olarak test eden bir şey üzerinde çalışması gerekiyor.

  3. Uygulanması isEmptydayanırcount

  4. Uygulamasına ==dayanır count.

BitVectorMevcut bir bit deseninden (a UInt64) bir yapı oluşturan özel bir API kullanarak bu sorunu kısmen çözebildim . Bu, diğer başlatıcıları sınamadan değerleri başlatmama izin verdi, böylece yükselişimi "önyükleyebildim".

Birim testlerimin gerçekten birim testler olması için, kendimi prod ve test kodumu önemli ölçüde karmaşık hale getiren bir grup hack yaparken buluyorum.

Bu tür sorunları tam olarak nasıl çözüyorsunuz?


20
"Birim" terimiyle ilgili çok dar bir görüş alıyorsunuz. BitVectorünite testi için mükemmel bir ünite büyüklüğüdür ve derhal sorunlu üyelerin BitVectoranlamlı testler yapmak için birbirlerine ihtiyaç duyduğu sorunları çözer .
Bart van Ingen Schenau,

Çok fazla uygulama detayı biliyorsunuz. Gelişiminiz gerçekten test odaklı mı?
herby,

@herby Hayır, bu yüzden pratik yapıyorum. Bu gerçekten ulaşılamaz bir standart gibi gözükse de. Uygulamanın ne gerektireceğine dair net bir yaklaşım olmadan hiçbir şeyi programladığım hiçbir şey yapmıyorum.
Alexander - Monica'yı yeniden

@Alexander Bunu gevşetmeyi denemelisiniz, aksi takdirde teste ilk denenecek, ancak teste dayalı olmayacak. "Belirsiz bir mağaza olarak bir 64 bit int ile bir bit vektör yapacağım" deyin. bu noktadan sonra TDD kırmızı-yeşil-refaktörü birbiri ardına yapın. API ile birlikte uygulama detayları da testlerin çalıştırılmasını denemek (eski) ve bu testleri ilk etapta (ikincisi) yazmaktan ortaya çıkmalıdır.
herby 20

Yanıtlar:


66

Uygulama detayları hakkında çok fazla endişeleniyorsun.

O önemli değil , mevcut uygulamada , isEmptygüvenir countsen umursamayı gereken tüm kamu arayüzü: (veya aklınıza gelebilecek diğer her türlü ilişkiler). Örneğin, üç test yapabilirsiniz:

  • Yeni başlatılan bir nesnenin sahip olduğu count == 0.
  • Yeni başlatılmış bir nesnenin isEmpty == true
  • Yeni başlatılmış bir nesnenin bilinen boş nesneye eşit olması.

Bunların hepsi geçerli sınavlardır ve özellikle sınıfınızın içindekilerini yeniden yapılandırmaya karar verirseniz özellikle önem kazanırsınız, böylece güvenilmeyecek isEmptyfarklı bir uygulamaya sahip olabilirsiniz count- tüm sınavlarınız hala devam ettiği sürece, gerilememişsinizdir. şey.

Benzer şeyler diğer noktalarınız için de geçerlidir - iç uygulamanız için değil, genel arayüzü test etmeyi unutmayın. TDD'yi burada yararlı bulabilirsiniz, çünkü isEmptybunun için herhangi bir uygulama yazmadan önce ihtiyaç duyduğunuz testleri yazıyorsunuzdur .


6
@Alexander Ünite testinin net bir tanımına ihtiyaç duyan bir adam gibi konuşuyorsunuz. Bildiğim en iyisi Michael Feathers'tan
candied_orange

14
@Allexander, her bir metodu bağımsız olarak test edilebilir bir kod parçası olarak ele alıyorsunuz. Bu senin zorlukların kaynağı. Nesneyi bir bütün olarak test ederseniz, küçük parçalara bölmeye çalışmadan bu zorluklar kaybolur. Nesneler arasındaki bağımlılıklar, yöntemler arasındaki bağımlılıklar ile karşılaştırılamaz.
amon,

9
@Alexander "bir kod parçası" isteğe bağlı bir ölçümdür. Sadece bir değişkeni başlatarak birçok "kod parçası" kullanıyorsunuzdur. Önemli olan bir test olmasıdır yapışkan davranışsal birimi tarafından tanımlanan size .
Ant P,

9
“Okuduklarımdan, yalnızca bir kod parçasını kırarsanız, yalnızca bu kodla doğrudan ilişkili olan birim testlerinin başarısız olması gerektiği izlenimini edindim.” Bu takip edilmesi çok zor bir kural gibi görünüyor . (örneğin, bir vektör sınıfı yazarsanız ve indeks yönteminde bir hata yaparsanız, muhtemelen bu vektör sınıfını kullanan tüm kodlar arasında tonlarca kırılma olur)
jhominal

4
@Alexander Ayrıca, testler için "Düzenle, Hareket Et, Eşleştir" desenine bakın. Temel olarak, nesneyi ne durumda olursa olsun (Düzenleyin) ayarlayın, gerçekte test ettiğiniz yöntemi çağırın (Hareket) ve ardından durumunun beklentilerinize göre değiştiğini doğrulayın. (Assert). Ayarla'da kurduğunuz şeyler test için "ön koşullar" olacaktır.
GalacticCowboy

5

Bu tür sorunları tam olarak nasıl çözüyorsunuz?

Bir "birim testi" ne olduğu hakkındaki düşüncelerinizi gözden geçirin.

Bellekteki değişken verileri yöneten bir nesne temelde bir durum makinesidir. Herhangi bir değerli bir kullanım durumu bilgileri koymak için bir yöntem çağırmak, en azından gidiyor Yani içine nesnenin dışına bilgilerin bir kopyasını okumak için nesne ve bir yönteme çağırmak. İlginç kullanım durumlarında, veri yapısını değiştiren ek yöntemler de çağırıyor olacaksınız.

Uygulamada, bu genellikle gibi görünüyor

// GIVEN
obj = new Object(...)

// THEN
assert object.read(...)

veya

// GIVEN
obj = new Object(...)

// WHEN
object.change(...)

// THEN
assert object.read(...)

"Ünite testi" terminolojisi - pekala, çok iyi olmamanın uzun bir geçmişi var.

Onlara birim testleri diyorum, ancak birim testlerin kabul edilen tanımlarına pek uymuyor - Kent Beck, Örnek Test Etme Testi

Kent 1994 yılında SUnit'in ilk versiyonunu yazdı, TDU kitabının ilk taslağı 2002'nin başındaki JUnit'in limanı 1998 yılındaydı .

Bu testlerin ana fikri (daha doğrusu "programcı testleri" veya "geliştirici testleri" olarak adlandırılır), testlerin birbirinden izole edilmiş olmasıdır. Testler değişken veri yapılarını paylaşmaz, bu yüzden eşzamanlı olarak çalıştırılabilirler. Çözümü doğru bir şekilde ölçmek için testlerin belirli bir düzende çalışması gerektiğine dair hiçbir endişe yoktur.

Bu testler için birincil kullanım durumu, programcı tarafından düzenlemeler arasında kendi kaynak koduna göre çalıştırılmış olmalarıdır. Kırmızı yeşil refactor protokolünü uyguluyorsanız, beklenmeyen bir KIRMIZI her zaman son düzenlemenizdeki bir hatayı gösterir; Bu değişikliği geri aldığınızda testlerin YEŞİL olduğunu doğrulayın ve tekrar deneyin. Her hatanın tek bir test tarafından yakalandığı bir tasarıma yatırım yapmanın pek bir avantajı yoktur.

Tabii ki, bir birleştirme bir hataya neden oluyor, o zaman bu hatanın artık önemsiz olmadığını bulmak. Hataların yerelleştirilmesinin kolay olmasını sağlamak için atabileceğiniz çeşitli adımlar vardır. Görmek


1

Genel olarak (TDD kullanmasanız bile), nasıl uygulandığını bilmiyormuş gibi yaparken mümkün olduğunca testler yazmaya çalışmalısınız.

Eğer gerçekten TDD yapıyorsanız, durum böyle olmalı. Testleriniz programın çalıştırılabilir bir özelliğidir.

Test grafiğinin testlerin altında nasıl göründüğü, testlerin kendileri mantıklı ve iyi bakıldığı sürece ilgisizdir.

Bence senin sorunun TDD'yi anlaman.

Benim düşünceme göre, TDD çalışanlarını "karıştırıyorsun". "Test", "kod" ve "refactor" görevlileriniz birbirinden tamamen bağımsız olarak çalışır. Özellikle kodlama ve yeniden düzenleme yapan personelinizin, yeşil çalışmasını sağlamak / sürdürmek dışında testlere karşı hiçbir yükümlülüğü yoktur.

Tabii ki, tüm testlerin ortogonal ve birbirinden bağımsız olması ilke olarak iyi olacaktır. Ancak bu, diğer iki TDD personeliniz için bir endişe değildir ve kesinlikle testlerinizin kesin veya hatta zorunlu olarak gerçekçi bir zorunluluğu değildir. Temel olarak: Kimsenin sizden sormadığı bir koşulu yerine getirmek için kod kalitesi hakkındaki sağduyulu duygularınızı atmayı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.