Mockito.verify () ne zaman kullanılır?


201

JUnit test senaryolarını 3 amaç için yazıyorum:

  1. Kodumun, giriş kombinasyonlarının / değerlerinin tümü (veya çoğu) altında gerekli tüm işlevselliği karşıladığından emin olmak için.
  2. Uygulamayı değiştirebileceğimi ve tüm işlevlerimin hala tatmin olduğunu söylemek için JUnit test senaryolarına güvenebileceğimi sağlamak için.
  3. Kodun yeniden yazılması gerektiğinde, tüm kullanım durumlarının bir dokümanı olarak kodum işlenir ve yeniden düzenleme için bir spesifikasyon görevi görür. (Kodu yeniden düzenleyin ve jUnit testlerim başarısız olursa - muhtemelen bazı kullanım durumlarını kaçırdınız).

Neden veya ne zaman Mockito.verify()kullanılması gerektiğini anlamıyorum . Ben bkz zaman verify()çağrıldığını, benim jUnit uygulamasının farkına varmaktadır bana anlatıyor. (Böylece, işlevlerim etkilenmemiş olsa bile, uygulamamı değiştirmek jUnits'imi kıracaktır).

Arıyorum:

  1. Uygun kullanımı için kurallar ne olmalıdır Mockito.verify()?

  2. JUnits'in test edilen sınıfın uygulanmasından haberdar olması veya bunlarla sıkı sıkıya bağlı olması temelde doğru mudur?


1
Ben maruz kaldığınız aynı nedenle (birim test benim uygulama farkında olmak istemiyorum), ben mümkün olduğunca verify () kullanmaktan uzak kalmaya çalışın, ama hiçbir seçeneğim yok - stubbed void yöntemleri. Genel olarak, 'gerçek' çıktınıza katkıda bulunmadıkları hiçbir şeyi iade etmedikleri için konuşmak gerekirse; ama yine de, bunun çağrıldığını bilmelisin. Ancak katılıyorum, yürütme akışını doğrulamak için doğrulama kullanmanın bir anlamı yok.
Legna

Yanıtlar:


78

A sınıfı sözleşme, C tipi bir nesnenin B yöntemini çağırdığı gerçeğini içeriyorsa, C tipi bir alay yaparak ve B yönteminin çağrıldığını doğrulayarak bunu test etmelisiniz.

Bu, A sınıfı sözleşmenin C tipi (bir arabirim veya bir sınıf olabilir) hakkında konuşabileceği yeterli ayrıntıya sahip olduğu anlamına gelir. Evet, sadece "sistem gereksinimlerinin" ötesine geçen ve uygulamayı tanımlamanın bir yolundan geçen bir spesifikasyon seviyesinden bahsediyoruz.

Bu birim testleri için normaldir. Birim testi yaparken, her birimin "doğru olanı" yaptığını ve bunun genellikle diğer birimlerle etkileşimlerini içerdiğinden emin olmak istersiniz. Buradaki "birimler", sınıflar veya uygulamanızın daha büyük alt kümeleri anlamına gelebilir.

Güncelleme:

Bunun sadece doğrulama için değil, aynı zamanda saplama için de geçerli olduğunu hissediyorum. Bir ortak çalışma sınıfının yöntemini saptırır sapmaz, birim testiniz bir anlamda uygulamaya bağlı hale geldi. Böyle bir şekilde birim testlerin doğasında var. Mockito, doğrulama konusunda olduğu kadar stubbing ile ilgili olduğundan, Mockito'yu hiç kullanmanız, bu tür bir bağımlılığa rastlayacağınızı ima eder.

Deneyimlerime göre, bir sınıfın uygulamasını değiştirirsem, çoğu zaman, birim testlerinin uygulanmasını eşleşecek şekilde değiştirmem gerekir. Tipik olarak, olsa da, orada neler birim testleri envanterini değiştirmek zorunda olmayacak olan sınıf için; tabii ki değişikliğin nedeni daha önce test edemediğim bir durumun varlığıydı.

Yani birim testlerin konusu budur. Ortak sınıfların kullanım şekline bu tür bağımlılıktan muzdarip olmayan bir test gerçekten bir alt sistem testi veya bir entegrasyon testidir. Tabii ki, bunlar sıklıkla JUnit ile de yazılır ve sıklıkla alay kullanımını içerir. Kanımca, "JUnit", tüm farklı test türlerini üretmemizi sağlayan bir ürün için korkunç bir isim.


8
Teşekkürler, David. Bazı kod kümelerini taradıktan sonra, bu yaygın bir uygulama gibi görünüyor - ama benim için bu, birim testleri oluşturma amacını yeniyor ve sadece çok az değer için bakım yükünü ekliyor. Alayların neden gerekli olduğunu ve testin yürütülmesine ilişkin bağımlılıkların neden ayarlanması gerektiğini anlıyorum. Ancak yöntem bağımlılığınınA.XYZ () 'nin yürütüldüğünü doğrulamak, testleri çok kırılgan kılıyor.
Russell

@Russell "C tipi", bir kitaplığın etrafındaki veya uygulamanızın bazı farklı alt sistemlerinin etrafındaki bir sarmalayıcı için bir arabirim olsa bile?
Dawood ibn Kareem

1
Bazı alt sistem veya hizmet çağrıldığından emin olmak için tamamen yararsız olduğunu söyleyemem - sadece etrafında bazı yönergeler olmalı (onları formüle etmek istediğim şeydi). Örneğin: (Muhtemelen aşırı basitleştiriyorum) Diyelim, kodumda StrUtil.equals () kullanıyorum ve uygulamada StrUtil.equalsIgnoreCase () 'e geçmeye karar verdim. JUnit doğruladı (StrUtil.equals ), uygulama doğru olsa da testim başarısız olabilir. Bu doğrulama çağrısı, IMO, kütüphaneler / alt sistemler için olsa da kötü bir uygulamadır. Öte yandan, closeDbConn çağrısının geçerli bir kullanıcı tabanı olabileceğinden emin olmak için doğrulama özelliğini kullanma.
Russell

1
Seni anlıyorum ve sana tamamen katılıyorum. Ancak, tarif ettiğiniz yönergeleri yazmanın tüm TDD veya BDD ders kitabını yazmaya dönüştüğünü de hissediyorum. Örneğinizi almak, aramak equals()ya equalsIgnoreCase()da asla bir sınıfın gereksinimlerinde belirtilen bir şey olmaz, bu yüzden asla kendi başına bir birim testi yapmazsınız. Bununla birlikte, "iş bittiğinde DB bağlantısının kapatılması" (bu uygulama açısından ne anlama gelirse gelsin), bir "iş gereksinimi" olmasa bile bir sınıfın gereksinimi olabilir. Benim için bu, sözleşme arasındaki ilişkiye iniyor ...
Dawood ibn Kareem

... iş gereksinimlerinde ifade edilen bir sınıfın ve o sınıfı test eden test yöntemleri kümesinin. Bu ilişkiyi tanımlamak TDD veya BDD ile ilgili herhangi bir kitapta önemli bir konudur. Mockito ekibinden biri wiki için bu konuda bir yazı yazabilirken, diğer birçok literatürden nasıl farklı olacağını göremiyorum. Bunun nasıl farklı olabileceğini görürseniz, bana bildirin ve belki birlikte çalışabiliriz.
Dawood ibn Kareem

60

David'in cevabı elbette doğru ama neden bunu isteyeceğinizi tam olarak açıklamıyor.

Temel olarak, birim testi yaparken, bir işlevsellik birimini tek başına test ediyorsunuz. Girişin beklenen çıktıyı üretip üretmediğini test edersiniz. Bazen yan etkileri de test etmeniz gerekir. Özetle, doğrulama bunu yapmanıza izin verir.

Örneğin, bir DAO kullanarak bir şeyler depolaması gereken biraz iş mantığınız var. Bunu, DAO'yu başlatan, iş mantığına bağlayan ve daha sonra beklenen şeylerin depolanıp depolanmadığını görmek için veritabanında poke eden bir entegrasyon testi kullanarak yapabilirsiniz. Bu artık bir birim test değil.

Veya DAO ile alay edebilir ve beklediğiniz şekilde çağrıldığını doğrulayabilirsiniz. Mockito ile bir şeyin çağrıldığını, ne sıklıkta çağrıldığını doğrulayabilir ve hatta belirli bir şekilde çağrıldığından emin olmak için parametrelerde eşleştiriciler kullanabilirsiniz.

Bu şekilde yapılan birim testlerin flip tarafı, testleri yeniden düzenlemeyi biraz daha zorlaştıran uygulamaya bağladığınızdır. Öte yandan, iyi bir tasarım kokusu, onu düzgün bir şekilde kullanmak için gereken kod miktarıdır. Testlerinizin çok uzun olması gerekiyorsa, muhtemelen tasarımda bir sorun var. Bu yüzden test edilmesi gereken çok sayıda yan etkisi / karmaşık etkileşimi olan kod muhtemelen iyi bir şey değildir.


30

Bu harika bir soru! Bence bunun temel nedeni aşağıdaki, biz sadece birim test için JUnit kullanıyoruz. Bu yüzden soru bölünmelidir:

  • Entegrasyonumda (veya birimden daha yüksek testlerde) Mockito.verify () yöntemini kullanmalı mıyım ?
  • Kara kutu birim testlerimde Mockito.verify () kullanmalı mıyım ?
  • Beyaz kutu birim testlerimde Mockito.verify () kullanmalı mıyım ?

Biz daha yüksek-daha-biriminin test yok sayacak eğer öyleyse, soru, "rephrased edilebilir kullanarak beyaz kutu Biraz yapabilir Mockito.verify () ile birim test ünitesi testi ve benim olabilir uygulanması arasındaki büyük çift oluşturur gri-box" " Birim testi ve bunun için hangi temel kuralları kullanmalıyım ".

Şimdi, tüm bu adım adım ilerleyelim.

* - Mockito.verify () 'i entegrasyonumda (veya herhangi bir birimden daha yüksek testlerde) kullanmalı mıyım? * Cevabın açıkça hayır olduğunu düşünüyorum, ayrıca bunun için alay kullanmamalısınız. Testiniz mümkün olduğunca gerçek uygulamaya yakın olmalıdır. Uygulamanın izole kısmı değil, tam kullanım durumunu test ediyorsunuz.

* kara kutu vs beyaz kutu birim testi * Eğer gerçekten ne yaptığınızı kara kutu yaklaşımı kullanıyorsanız , (tüm eşdeğerlik sınıfları) girdi, bir durum ve beklenen çıktı alacak testleri sağlar. Bu yaklaşımda genel olarak alayların kullanılması haklıdır (sadece doğru şeyi yaptıklarını taklit edersiniz; onları test etmek istemezsiniz), ancak Mockito.verify () öğesini çağırmak gereksizdir.

Gerçekten ne yaptığınızı beyaz kutu yaklaşımı kullanıyorsanız , ünitenizin davranışını test ediyorsunuz. Mockito.verify () işlevini çağırmak bu yaklaşımda çok önemlidir, ünitenizin beklediğiniz gibi davrandığını doğrulamalısınız.

gri kutu testi için temel kurallar Beyaz kutu testi ile ilgili sorun, yüksek bir bağlantı oluşturmasıdır. Olası bir çözüm, beyaz kutu testi değil, gri kutu testi yapmaktır. Bu, siyah-beyaz kutu testlerinin bir kombinasyonudur. Ünitenizin davranışını beyaz kutu testinde olduğu gibi gerçekten test ediyorsunuz, ancak genel olarak mümkün olduğunda uygulamadan bağımsız hale getiriyorsunuz . Mümkün olduğunda, kara kutu durumunda olduğu gibi bir kontrol yapacaksınız, sadece çıktının beklenen şey olduğunu iddia ediyor. Yani, sorunuzun özü mümkün olduğu zamandır.

Bu gerçekten zor. İyi bir örneğim yok, ama size örnekler verebilirim. Yukarıda equals () vs equalsIgnoreCase () ile belirtilen durumda Mockito.verify () öğesini çağırmamalısınız, sadece çıktıyı onaylamalısınız. Bunu başaramadıysanız, kodunuzu daha küçük birime ayırabilirsiniz. Öte yandan, bazı @Servisiniz olduğunu ve @Servis'e sarılmış olan @ Web-Service'i yazdığınızı varsayalım - bu, @Service'e yapılan tüm çağrıları devrediyor (ve bazı ekstra hata işlemleri yapıyor). Bu durumda Mockito.verify () işlevini çağırmak önemlidir, @Serive için yaptığınız tüm kontrollerinizi çoğaltmamalısınız, doğru parametre parametresi listesiyle @Service'e çağırdığınızı doğrulayın.


Gri kutu testi biraz tuzaktır. DAO'lar gibi şeylerle kısıtlama eğilimindeyim. Çok sayıda gri kutu testi, neredeyse tam bir birim test eksikliği ve greybox testlerinin sözde test ettiklerine güven eksikliğini telafi etmek için çok fazla blackbox testi nedeniyle son derece yavaş yapılara sahip bazı projelerde bulundum.
Jilles van Gurp

Benim için bu en iyi cevap çünkü çeşitli durumlarda Mockito.when () ne zaman kullanılacağına cevap veriyor. Aferin.
Michiel Leegwater

8

Klasik bir yaklaşımın bakış açısından kesinlikle haklı olduğunuzu söylemeliyim:

  • Uygulamanızın ilk önce iş mantığını oluşturursanız (veya değiştirirseniz) ve daha sonra bunları testler (kabul et) ile kaplarsanız ( Son Test yaklaşımı ), testlerin yazılımınızın nasıl çalıştığı hakkında herhangi bir şey bilmesine izin vermek çok acı verici ve tehlikeli olacaktır. giriş ve çıkışların kontrolü.
  • Test Odaklı bir yaklaşım uyguluyorsanız , ilk önce yazılan, değiştirilen ve yazılımınızın işlevselliğinin kullanım durumlarını yansıtan testleriniz yapılır . Uygulama testlere bağlıdır. Bu bazen, yazılımınızın belirli bir şekilde uygulanmasını istediğiniz anlamına gelir; örneğin, başka bir bileşenin yöntemine güvenin veya hatta belirli bir süre olarak adlandırın. Mockito.verify () işte burada devreye giriyor!

Evrensel bir araç olmadığını hatırlamak önemlidir. Yazılım türü, boyutu, şirket hedefleri ve pazar durumu, takım becerileri ve daha birçok şey, sizin durumunuzda hangi yaklaşımın kullanılacağına karar verir.


0

Bazı insanların söylediği gibi

  1. Bazen iddia edebileceğiniz doğrudan bir çıktınız olmayabilir.
  2. Bazen test edilen yönteminizin, ortak çalışanlarına (alay etmekte olduğunuz) doğru dolaylı çıktılar gönderdiğini onaylamanız gerekir.

Yeniden düzenleme sırasında testlerinizi kırma konusundaki endişenizle ilgili olarak, alay / saplama / casus kullanırken biraz beklenir. Yani tanımı ve Mockito gibi belirli bir uygulama ile ilgili değil. Ama bu şekilde düşünebilirim - Yolda büyük değişiklikler yaratacak bir üstlenmeden yapmak gerekiyorsa yöntem çalışır, bir TDD yaklaşım üzerinde bunu yapmak için iyi bir fikir testinizi değiştirebilir, yani bir ilk tanımlamak yeni davranış (sınamayı geçemez) ve sonra değişiklikleri yapın ve sınamayı yeniden iletin.


0

Çoğu durumda insanlar Mockito.verify'ı kullanmaktan hoşlanmadığında, bunun nedeni test edilen birimin yaptığı her şeyi doğrulamak için kullanılmasıdır ve bu, testte bir şey değişirse testinizi uyarlamanız gerektiği anlamına gelir. Ama bunun bir sorun olduğunu düşünmüyorum. Bir yöntemin testini değiştirmeye gerek kalmadan ne yaptığını değiştirmek istiyorsanız, temel olarak, yönteminizin yaptığı her şeyi test etmeyen testler yazmak istediğiniz anlamına gelir, çünkü değişikliklerinizi test etmesini istemezsiniz . Ve bu yanlış düşünme şeklidir.

Gerçekten sorun olan, yönteminizin ne yaptığını değiştirebilmeniz ve işlevselliği tamamen kapsadığı düşünülen bir birim testinin başarısız olmamasıdır. Bu, değişikliğinizin amacı ne olursa olsun, değişikliğinizin sonucunun test tarafından kapsanmadığı anlamına gelir.

Bu nedenle, olabildiğince alay etmeyi tercih ediyorum: veri nesnelerinizi de alay et. Bunu yaparken, yalnızca diğer sınıfların doğru yöntemlerinin çağrıldığını kontrol etmek için değil, aynı zamanda iletilen verilerin bu veri nesnelerinin doğru yöntemleri aracılığıyla toplandığını kontrol etmek için doğrulamayı kullanabilirsiniz. Ve bunu tamamlamak için, aramaların gerçekleşme sırasını test etmelisiniz. Örnek: Bir db varlık nesnesini değiştirir ve daha sonra bir havuz kullanarak kaydederseniz, nesne ayarlayıcılarının doğru verilerle çağrıldığını ve deponun save yönteminin çağrıldığını doğrulamak yeterli değildir. Yanlış sırayla çağrılırlarsa, yönteminiz hala yapılması gerekeni yapmaz. Yani, Mockito.verify kullanmıyorum ama tüm alaylarla bir inOrder nesnesi oluşturuyorum ve bunun yerine inOrder.verify kullanıyorum. Ve bunu tamamlamak istiyorsanız, Mockito'yu da aramalısınız. sonundaNoMoreInteractions doğrulamak ve tüm alay etmek. Aksi takdirde, birisi test etmeden yeni işlevsellik / davranış ekleyebilir; bu, kapsama istatistikleriniz% 100 olabilir ve yine de iddia edilmemiş veya doğrulanmamış bir kod biriktirdiğiniz anlamına gelir.

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.