Nesnelerin alay edilmesinin zor olduğu bir sistemi nasıl test ederim?


34

Aşağıdaki sistemle çalışıyorum:

Network Data Feed -> Third Party Nio Library -> My Objects via adapter pattern

Geçenlerde, kullandığım kütüphanenin sürümünü güncellediğimde, başka şeylerin yanı sıra, zaman damgasının (üçüncü taraf kütüphanesinin döndüğü long) dönemin ardından milisaniyeden devir sonrası nanosaniye olarak değiştirilmesine neden olan bir sorun yaşadık .

Sorun:

Üçüncü şahıs kütüphanesinin nesnelerine adanmış testler yazarsam, üçüncü şahıs kütüphanesinin objeleri hakkında bir hata yaptıysam testim yanlış olur. Örneğin, zaman damgalarının hassasiyetini değiştirdiğini fark etmedim, bu da ünite testinde değişiklik yapılması gerekmesine neden oldu, çünkü sahte bilgisayarım yanlış veriyi geri verdi. Bu, kütüphanede bir hata değil , belgelerde bir şeyleri özlediğim için oldu.

Sorun şu ki, bu veri yapılarında yer alan verilerden emin olamıyorum çünkü gerçek veri akışı olmadan gerçek veri üretemiyorum. Bu nesneler büyük ve karmaşıktır ve içerisinde birçok farklı veri parçası vardır. Üçüncü şahıs kütüphanesinin dokümantasyonu yetersiz.

Soru:

Bu davranışı test etmek için testleri nasıl ayarlayabilirim? Bu sorunu bir birim testinde çözebileceğimden emin değilim, çünkü testin kendisi kolayca yanlış olabilir. Ek olarak, entegre sistem büyük ve karmaşıktır ve bir şeyi kaçırmak kolaydır. Örneğin, yukarıdaki durumda, birkaç yerde zaman damgası işlemeyi doğru ayarlamıştım, ancak bir tanesini özledim. Sistem, entegrasyon testimde çoğunlukla doğru şeyleri yapıyor gibiydi, ancak bunu üretime dağıttığımda (daha fazla veriye sahip) sorun ortaya çıktı.

Şu anda entegrasyon testlerim için bir işlemim yok. Sınama esastır: ünite testlerini iyi tutmaya çalışın, işler bozulduğunda daha fazla test ekleyin, sonra test sunucuma konuşlandırın ve işlerin mantıklı göründüğünden emin olun, daha sonra üretime dağıtın. Bu zaman damgası sorunu, alaylar yanlış oluşturulduğundan ünite sınamalarını geçti, daha sonra herhangi bir ani ve belirgin soruna neden olmadığından entegrasyon testini geçti. QA departmanım yok.


3
Gerçek bir veri beslemesini "kaydedebilir" ve daha sonra üçüncü parti kütüphanesinde "oynayabilir" mi?
Idan Arye

2
Birisi böyle problemler üzerine bir kitap yazabilir. Aslında, Michael Feathers tam da bu kitabı yazdı : c2.com/cgi/wiki?WorkingEffectivelyWithLegacyCode İçinde, zor bağımlılıkları kırma teknikleriyle kodun daha fazla test edilebilir hale gelmesini sağlar.
cbojar

2
Üçüncü parti kütüphanesinin etrafındaki adaptör? Evet, tam olarak bunu tavsiye ediyorum. Bu birim testleri kodunuzu geliştirmez. Daha güvenilir ve daha bakımsız hale getiremezler. Bu noktada başka birinin kodunu kısmen kopyaladınız; Bu durumda, kötü yazılmış bazı kodları seslerinden çoğaltıyorsunuzdur. Bu net bir kayıp. Cevapların bazıları bazı entegrasyon testlerinin yapıldığını gösteriyor; "Sadece bu işe yarıyor mu?" aklı kontrol. İyi testler zordur ve iyi kod kadar beceri ve sezgi gerektirir.
jpmc26

4
Yerleşiklerin kötülüğünün mükemmel bir örneği. Neden kütüphanesi dönmez Timestamp(adlandırılmış yöntemleri (istedikleri temsilini içeren) sınıf ve sağlamak .seconds(), .milliseconds(), .microseconds(), .nanoseconds()) ve ders adlandırılmış kurucular. O zaman sorun olmazdı.
Matthieu M.

2
"Kodlamadaki tüm problemler bir dolaylı katmanla çözülebilir (elbette, çok fazla dolaylı katman sorunu hariç)" deyişi akla geliyor.
Dan Pantry

Yanıtlar:


27

Zaten durum tespiti yapıyormuşsun gibi geliyor. Fakat ...

En pratik düzeyde, her zaman kendi kodunuz için süitinizde her iki "tam döngü" entegrasyon testine iyi bir avuç ekleyin ve ihtiyaç duyduğunuzdan daha fazla iddia yazın. Özellikle, tam oluşturma-okuma- [do_stuff] -validate döngüsü gerçekleştiren bir avuç testiniz olmalıdır.

[TestMethod]
public void MyFormatter_FormatsTimesCorrectly() {

  // this test isn't necessarily about the stream or the external interpreter.
  // but ... we depend on them working how we think they work:
  var stream = new StreamThingy();
  var interpreter = new InterpreterThingy(stream);
  stream.Write("id-123, some description, 12345");

  // this is what you're actually testing. but, it'll also hiccup
  // if your 3rd party dependencies introduce a breaking change.
  var formatter = new MyFormatter(interpreter);
  var line = formatter.getLine();
  Assert.equal(
    "some description took 123.45 seconds to complete (id-123)", line
  );
}

Ve zaten böyle bir şey yapıyormuşsun gibi geliyor. Sadece lapa lapa ve / veya karmaşık bir kütüphane ile uğraşıyorsunuz. Ve bu durumda, birkaç "kütüphanenin nasıl işlediğini" atmak iyidir; hem kütüphaneyi anladığınızı doğrulayan hem de kütüphanenin nasıl kullanılacağına örnek olarak sunulan test türleri.

Bir JSON ayrıştırıcısının bir JSON dizesindeki her "türü" nasıl yorumladığını anlamanız ve buna bağlı olmanız gerektiğini varsayalım . Süitinize böyle bir şey eklemek yararlı ve önemsizdir:

[TestMethod]
public void JSONParser_InterpretsTypesAsExpected() {
  String datastream = "{nbr:11,str:"22",nll:null,udf:undefined}";
  var o = (new JSONParser()).parse(datastream);

  Assert.equal(11, o.nbr);
  Assert.equal(Int32.getType(), o.nbr.getType());
  Assert.equal("22", o.str);
  Assert.equal(null, o.nll);
  Assert.equal(Object.getType(), o.nll.getType());
  Assert.isFalse(o.KeyExists(udf));
}

Ancak ikincisi, her türlü ve neredeyse her türlü titizlikle otomatik testlerin sizi tüm hatalara karşı koruyamayacağını unutmayın. Sorunları keşfederken testler eklemek tamamen yaygındır . QA departmanına sahip olmamak, bu sorunların çoğunun son kullanıcılar tarafından keşfedileceği anlamına gelir.

Ve önemli ölçüde, bu normal.

Üçüncüsü, bir kütüphane alan veya metodu yeniden adlandırmadan ya da başka bir şekilde bağımlı kodları "kırmadan" (belki türünü değiştirerek) olmadan bir dönüş değerinin veya alanın anlamını değiştirdiğinde, o yayıncıdan çok mutsuz olurum. Ve tartışmalıyım ki, eğer bir tane varsa, belki de changelog'u okumuş olsanız bile, muhtemelen stresinizin bir kısmını yayıncıya iletmelisiniz. Umarım yapıcı eleştiriye ihtiyaçları vardır.


Uh, keşke bir json stringi kütüphaneye beslemek kadar basit olsaydı. Değil. (new JSONParser()).parse(datastream)Verileri doğrudan NetworkInterfacea'dan alıkoydukları için eşdeğerini yapamam , çünkü gerçek ayrıştırmayı yapan tüm sınıflar özel paket ve korumalıdır.
durron597

Ayrıca, değişiklikler, belgelemedikleri diğer baş ağrıları arasında, zaman damgalarını ms'den ns'ye değiştirdikleri gerçeğini içermiyordu. Evet, onlardan çok mutsuzum ve bunu onlara ifade ettim.
durron597

@ durron597 Ah, neredeyse asla. Ancak, ilk kod örneğindeki gibi temel veri kaynağını taklit edebilirsiniz. ... Nokta:, mümkün olduğunda kütüphaneye anlayışınızı test etmek ve sadece unutmayın mümkün olduğunda tam döngü entegrasyon testleri yapmak olacak hala vahşi doğaya hataları bildirin. Ayrıca, üçüncü taraf tedarikçilerinizin , görünmez ve değişiklikleri kırmaktan sorumlu olmaları gerekir .
svidgen

@ durron597 Aşina NetworkInterfaceolmadığım bir şey ... arayüzünü localhost üzerindeki bir bağlantı noktasına bağlayarak veri besleyebileceğiniz bir şey mi?
svidgen

NetworkInterface. Doğrudan bir ağ kartı ile çalışmak ve üzerindeki soketleri açmak vb. İçin düşük seviyeli bir nesnedir.
durron597

11

Kısa cevap: Zor. Muhtemelen iyi cevaplar yokmuş gibi hissediyorsunuzdur ve bunun nedeni kolay cevaplar olmamasıdır.

Uzun cevap: @ptyx'in dediği gibi, birim testlerin yanı sıra sistem testlerine ve entegrasyon testlerine ihtiyacınız var:

  • Ünite testleri hızlı ve kullanımı kolaydır. Ayrı ayrı kod bölümlerinde böcekleri yakalarlar ve bunları çalıştırmak için alay kullanırlar. Gerekirse, kod parçaları arasındaki uyumsuzlukları yakalayamazlar (milisaniye ve nanosaniye gibi).
  • Entegrasyon testleri ve sistem testleri yavaş (er) ve zor (er), ancak daha fazla hata alıyor.

Bazı özel öneriler:

  • Mümkün olduğu kadar çok sistemi çalıştırmak için bir sistem testi yaptırmanın bir faydası var. Davranışın çoğunu doğrulayamıyorsa veya sorunu tam olarak belirlemekte iyi değil. (Micheal Feathers bunu daha çok Legacy Code ile Etkili Çalışma konusunda tartışıyor .)
  • Test edilebilirliğe yatırım yapmak yardımcı olur. Burada kullanabileceğiniz çok sayıda teknik vardır: sürekli entegrasyon, komut dosyaları, VM'ler, oynatılacak araçlar, proxy veya ağ trafiğini yönlendirmek .
  • Test edilebilirliğe yatırım yapmanın avantajlarından biri (en azından benim için) açık olmayabilir: eğer testler sıkıcı, sinir bozucu veya hantalsa veya yazmak zahmetliyse, eğer baskı uygularsam bunları atlamak benim için çok kolaydır veya yorgun. Aşağıya testleri tutulması "O kadar kolay mazeret yok işte değil eşiği bunu" önemli.
  • Mükemmel yazılım mümkün değil. Her şey gibi, test etmek için harcanan çaba bir değişimdir ve bazen çabaya değmez. Kısıtlamalar (bir QA departmanının olmayışı gibi) mevcuttur. Böceklerin olacağını, iyileşeceğini ve öğreneceğini kabul et.

Bir problem ve çözüm alanı hakkında öğrenme aktivitesi olarak tanımlanan programlamayı gördüm. Herşeyi vaktinden önce mükemmel yapmak mümkün olmayabilir, ancak olaydan sonra öğrenebilirsiniz. ("Zaman damgası işlemeyi birkaç yerde sabitledim, ancak birini kaçırdım. Zaman damgası işlemeyi daha açık ve daha zor hale getirmek veya daha merkezi hale getirmek için veri türlerimi veya sınıflarımı değiştirebilir miyim, yoksa değiştirecek tek bir yerim olabilir mi? zaman damgası kullanımının daha fazla yönünü doğrulamak için yaptığım testler? Gelecekte bunu kolaylaştırmak için test ortamımı basitleştirebilir miyim? Bunu kolaylaştıracak bir araç hayal edebilir miyim? Öyleyse, böyle bir aracı Google'da bulabilir miyim? " Vb.)


7

Kütüphanenin ... (üçüncü parti kütüphanesinin döndüğü zaman damgalarının long) zamandan sonra milisaniyeden, çağdan sonra nanosaniye olarak değiştirilmesine neden olan sürümünü güncelledim .

...

Bu kütüphanede bir hata değil

Sana burada kesinlikle katılmıyorum. Bu kütüphanede bir hata olduğunu , aslında oldukça sinsi bir. Dönen değerin semantik türünü değiştirdiler, ancak geri dönüş değerinin programatik türünü değiştirmediler. Bu, her ne kadar tahribata yol açabilir ki, özellikle küçük bir sürüm çarpması olsaydı, ama aynı zamanda büyük bir savaş olsa bile.

Diyelim ki kütüphane bir tür MillisecondsSinceEpoch, basit bir paketleyici döndürdü long. Bunu bir NanosecondsSinceEpochdeğere değiştirdiklerinde kodunuz derlenemedi ve açıkça değişiklik yapmanız gereken yerleri işaret etti. Değişiklik programınızı sessizce bozamadı.

Daha da iyisi, TimeSinceEpochara yüzünü, #toLongNanosecondsyöntem boyunca bir yöntem ekleyerek #toLongMilliseconds, kodunuzda herhangi bir değişiklik yapmayı gerektirmemesi gibi, daha fazla hassasiyet eklendikçe, kendi arayüzünü uyarlayabilecek bir nesne olacaktır .

Bir sonraki sorun, kütüphaneye güvenilir bir entegrasyon testi setinizin olmamasıdır. Bunları yazmalısın. Daha iyi olması, uygulamanızın geri kalanından uzaklaşmak için bu kitaplığın etrafında bir arayüz oluşturmak olacaktır. Başka birkaç cevap da bu konuyu ele alıyor (ve ben yazdıkça daha da ortaya çıkıyor). Entegrasyon testleri, birim testlerinizden daha az sıklıkta yapılmalıdır. Bu nedenle tampon katmana sahip olmanız yardımcı olur. Entegrasyon testlerinizi ayrı bir alana ayırın (veya farklı isimlendirin) böylece gerektiğinde çalıştırabilirsiniz, ancak ünite testinizi her çalıştırdığınızda değil.


2
@ durron597 Hala bunun bir hata olduğunu iddia ediyorum. Belgelendirme eksikliğinin ötesinde, neden beklenen davranışı değiştirelim? Neden yeni bir hassasiyet sağlayan ve eski yöntemin hala millis sağlamasına izin veren yeni bir yöntem değil? Ve neden derleyicinin geri dönüş tipindeki bir değişiklikle sizi uyarması için bir yol sağlamıyorsunuz? Bunu sadece belgelerde değil, kodun kendisinde daha net hale getirmek için çok fazla bir şey gerekmez.
cbojar

1
@gbjbaanb, “kötü serbest bırakma uygulamaları var” bana bir hata gibi görünüyor
Arturo Torres Sánchez

2
@gbjbaanb 3. parti bir kütüphane, kullanıcıları ile “sözleşme” yapmalıdır. Bu sözleşmeyi bozmak - belgelenmiş olsun veya olmasın - bir hata olarak kabul edilebilir / olmalıdır. Diğerleri söylediler eğer, gereken şeyleri değiştirmek, yeni bir fonksiyon / yöntem ile sözleşme eklemek (bütün bakınız ...Ex()Win32API içinde yöntemlerle). Bu mümkün değilse, işlevin (veya dönüş türünün) ismini değiştirerek sözleşmeyi "kırmak", davranışı değiştirmekten daha iyi olurdu.
TripeHound

1
Bu kütüphanede bir hatadır. Nanosaniye uzun süre kullanmak onu zorluyor.
Joshua,

1
@gbjbaanb Beklenmedik olsa bile, amaçlanan davranış olduğundan beri bir hata olmadığını söylüyorsunuz. Bu anlamda bir uygulama hatası değil , aynı şekilde bir hata. Buna bir tasarım hatası veya bir arayüzleme hatası denilebilir . Hatalar, açık birimlerden ziyade uzun süren ilkel bir takıntıya maruz kalması, açık bir şekilde iç uygulamasının ayrıntılarını dışa aktarması (verilerin belirli bir birimin uzun bir süre depolanması) ihracı ve ihlal etmesi nedeniyle ince bir birim değişikliği ile en az şaşkınlık ilkesi.
cbojar

5

Entegrasyon ve sistem testlerine ihtiyacınız var.

Birim testleri, kodunuzun beklediğiniz gibi davrandığını doğrulamak konusunda mükemmeldir. Fark ettiğiniz gibi, varsayımlarınıza meydan okumak veya beklentilerinizin aklı başında olmasını sağlamak için hiçbir şey yapmaz.

Ürününüz dış sistemler ile çok az etkileşime sahip değilse veya çok iyi bilinen, sağlam ve güvenli bir şekilde alay edilebilecekleri belgelenmiş sistemler ile etkileşime girmezse (bu nadiren gerçek dünyada olur) - ünite testleri yeterli değildir.

Testleriniz ne kadar yüksek olursa, sizi beklenmedik durumlara karşı o kadar çok korurlar. Bu bir maliyetle gelir (kolaylık, hız, kırılganlık ...), bu nedenle birim testler testinizin temeli olarak kalmalıdır, ancak - sonunda - yakalamada çok uzun süren küçük bir insan testini de içeren başka katmanlara ihtiyacınız vardır. kimsenin düşünmediği aptal şeyler.


2

En iyisi minimal bir prototip oluşturmak ve kütüphanenin tam olarak nasıl çalıştığını anlamak olacaktır. Bunu yaparak, kötü belgelemeye sahip kütüphane hakkında biraz bilgi edineceksiniz. Bir prototip, bu kütüphaneyi kullanan ve işlevselliği yapan minimalist bir program olabilir.

Aksi halde, yarı tanımlanmış gereklilikler ve sistemin zayıf anlaşılmasıyla birim testleri yazmak hiç mantıklı değildir.

Özel probleminize gelince - yanlış ölçümlerin kullanılması hakkında: Ben bunu bir gereksinim değişikliği olarak kabul ediyorum. Sorunu tanıdığınızda, birim testlerini ve kodu değiştirin.


1

Popüler ve sağlam bir kütüphane kullanıyor olsaydınız, belki de sizin için kötü hileler çalmayacağını varsayabilirdiniz. Ancak, anlattıklarınız gibi şeyler bu kitaplıkta gerçekleşirse, açıkçası, bu bir değil. Bu kötü deneyimden sonra, bu kütüphane ile olan etkileşimlerinizde bir şeyler yanlış gittiğinde, yalnızca bir hata yapma ihtimalini değil, aynı zamanda kütüphanenin bir hata yapmış olabileceğini de incelemelisiniz. Diyelim ki bu sizin “emin olmadığınız” bir kütüphane.

“Emin olamadığımız” kütüphaneler ile birlikte kullanılan tekniklerden biri, kütüphanemizin sunduğu işlevselliği özetleyen sistemimiz ve söz konusu kütüphaneler arasında bir ara katman oluşturmak, kütüphaneden beklentilerimizin doğru olduğunu ve aynı zamanda büyük ölçüde basitleştirdiğini iddia ediyor. Gelecekteki hayatımız, bu kütüphaneye çizme vermeye karar vermeli ve onu daha iyi davranan başka bir kütüphaneyle değiştirmeliyiz.


Bu soruya gerçekten cevap vermiyor. Zaten kütüphaneyi sistemimden ayıran bir katmana sahibim, ancak sorun şu ki, kütüphane uyarıda bulunmadan üzerime değiştiğinde soyutlama katmanımın "hataları" olabilir.
durron597

1
@ durron597 O zaman belki katman kütüphaneyi uygulamanızın kalanından yeterince izole etmiyordur. Bu katmanı test etmekte zorlanıyorsanız, davranışı basitleştirmeniz ve temel alınan verileri daha güçlü bir şekilde yalıtmanız gerekebilir.
cbojar

@Cbojar ne dedi. Ayrıca, yukarıdaki metinde fark edilmemiş olabilecek bir şeyi tekrar etmeme izin verin: Kullandığınız assertdile bağlı olarak anahtar kelime (veya işlev veya tesis) arkadaşınızdır. Birim / entegrasyon testlerindeki iddialardan bahsetmiyorum, yalıtım katmanının iddialarla çok ağır olması gerektiğini söylüyorum, kütüphanenin davranışı hakkında iddia edilebilir her şeyi iddia ediyorum.
Mike Nakis

Bu iddialar mutlaka üretim çalışmalarında yerine getirilmemektedir, ancak test sırasında uygulanmaktadır, yalıtım katmanınızın beyaz kutu görüntüsünü alır ve bu nedenle katmanınızın kitaplıktan aldığı bilginin (mümkün olduğunca) elde edildiğinden emin olabilir ses.
Mike Nakis
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.