İç testleri test etmeden DI kullanan bir sınıfı test etme


12

1 ana sınıf ve 2 küçük sınıfta yeniden düzenlenmiş bir sınıf var. Ana sınıflar veritabanını kullanır (sınıflarımın çoğu gibi) ve bir e-posta gönderir. Yani ana sınıf bir IPersonRepositoryve bir IEmailRepositoryenjekte var, bu da 2 küçük sınıfa gönderir.

Şimdi ana sınıfı ünite testi yapmak istiyorum ve sınıfın iç işleyişini ünite test etmemeyi öğrendim, çünkü ünite testlerini bozmadan iç işleri değiştirebilmeliyiz.

Sınıf kullanır Ama IPersonRepositorybir IEmailRepositoryben VAR bazı yöntemler için (sahte / kukla) sonuçlarını belirtmek için IPersonRepository. Ana sınıf, bazı verileri mevcut verilere göre hesaplar ve döndürür. Bunu test etmek istersem, IPersonRepository.GetSavingsByCustomerIdx döndürmesini belirtmeden nasıl test yazabileceğimi göremiyorum . Ama sonra benim birim testim içsel çalışmaları 'bilir', çünkü hangi yöntemlerin alay edip hangilerini alamayacağını 'bilir'.

İçselleri bilmeden, bağımlılıkları enjekte edilen bir sınıfı nasıl test edebilirim?

arka fon:

Deneyimlerime göre, bu gibi birçok test havuzlar için alaylar oluşturur ve daha sonra alaylar için doğru verileri sağlar veya yürütme sırasında belirli bir yöntemin çağrılıp çağrılmadığını test eder. Her iki durumda da, test içselleri bilir.

Şimdi, testin uygulama hakkında bilmemesi gereken teori hakkında (daha önce duyduğum) bir sunum gördüm. Birincisi, nasıl çalıştığını test ettiğiniz için değil , aynı zamanda şimdi uygulamayı değiştirdiğinizde tüm birim testleri başarısız oluyor çünkü uygulama hakkında 'bilgi'. Testler kavramının uygulamanın farkında olmamasına rağmen, bunu nasıl başaracağımı bilmiyorum.


1
Ana sınıfınız bir IPersonRepositorynesne beklediği anda , bu arayüz ve tarif ettiği tüm yöntemler artık "dahili" değildir, bu yüzden testin bir problemi değildir. Asıl sorunuz "herkese açık bir şekilde çok fazla maruz kalmadan sınıfları nasıl daha küçük birimlere yeniden aktarabilirim" olmalıdır. Cevap "bu arayüzleri yalın tut" (örneğin arayüz ayırma prensibine bağlı kalarak). Bu @ DavidArno'nun cevabında IMHO nokta 2 (Sanırım başka bir cevapta tekrarlamama gerek yok).
Doc Brown

Yanıtlar:


7

Ana sınıf, bazı verileri mevcut verilere göre hesaplar ve döndürür. Bunu test etmek istersem, IPersonRepository.GetSavingsByCustomerIdx döndürmesini belirtmeden nasıl test yazabileceğimi göremiyorum . Ama sonra benim birim testim içsel çalışmaları 'bilir', çünkü hangi yöntemlerin alay edip hangilerini alamayacağını 'bilir'.

Bunun "içselleri test etme" ilkesinin ihlali olduğu ve insanların göz ardı ettiği yaygın bir kural olduğu konusunda haklısınız.

İçselleri bilmeden, bağımlılıkları enjekte edilen bir sınıfı nasıl test edebilirim?

Bu ihlali gidermek için kullanabileceğiniz iki çözüm vardır:

1) tam bir sahte sağlamak IPersonRepository. Şu anda açıklanan yaklaşımınız, yalnızca arayacağı yöntemleri alay ederek, denemeyi test edilen yöntemin iç işleyişi ile birleştirmektir. Tüm yöntemler için alaycı tedarik ederseniz IPersonRepository, bu kuplajı kaldırırsınız. İç çalışmalar alayı etkilemeden değişebilir, böylece testi daha az kırılgan hale getirir.

Bu yaklaşım, DI mekanizmasını basit tutma avantajına sahiptir, ancak arabiriminiz birçok yöntem tanımlarsa çok fazla iş oluşturabilir.

2) Enjekte etmeyin IPersonRepository, GetSavingsByCustomerIdyöntemi enjekte etmeyin veya tasarruf değeri enjekte etmeyin . Tüm arayüz uygulamalarının enjekte edilmesindeki sorun, daha sonra iki yaklaşımı karıştırarak bir "sor, söyleme" sistemi enjekte etmenizdir ("söyle, sorma"). Eğer bir "saf DI" yaklaşımı alırsanız, yöntem, bir nesne verilmesinden ziyade, tasarruf değeri isterse çağıracak kesin yöntemi sağlamalı (söylenmelidir) (daha sonra yöntemi çağırmak için etkili bir şekilde sormalıdır).

Bu yaklaşımın avantajı, alay ihtiyacını ortadan kaldırmasıdır (test edilen yönteme enjekte ettiğiniz test yöntemlerinin ötesinde). Dezavantajı, yöntemin değiştirilmesi gereklerine yanıt olarak yöntem imzalarının değişmesine neden olabilmesidir.

Her iki yaklaşımın da artıları ve eksileri vardır, bu yüzden ihtiyaçlarınıza en uygun olanı seçin.


1
Fikir 2'yi gerçekten seviyorum. Bunu neden daha önce düşünmediğimi bilmiyorum. Kendimi hiç bir IRepository'ye neden bir bağımlılık yarattığımı sormadan (birkaç durumda oldukça fazla yöntem imzası içerecek) IRepositories'e bağımlılıklar oluşturuyorum. 2 yöntem. Bu nedenle, bir IRepository enjekte etmek yerine, gereken (yalnızca) yöntem imzasını daha iyi enjekte edebilir veya geçebilirim.
Michel

1

Yaklaşımım, gerekli verileri içeren basit dosyalardan okunan havuzların 'sahte' sürümlerini oluşturmaktır.

Bu, testin her birinin alayın kurulumu hakkında 'bilmediği' anlamına gelir, ancak açık bir şekilde genel test projesi alaya referans verecek ve kurulum dosyalarına vb.

Bu, çerçevelerin alayının gerektirdiği karmaşık sahte nesne kurulumunu önler ve 'sahte' nesneleri UI testi ve benzerleri için uygulamanızın gerçek örneklerinde kullanmanızı sağlar.

'Sahte' test senaryosunuz için özel olarak ayarlanmak yerine tamamen uygulandığından, örneğin bir uygulama değişikliğinin GetSavingsForCustomer'ın artık müşteriyi de silmesi gerektiğini söyleyelim. Testleri kırmayacak (elbette testleri gerçekten ihlal etmedikçe) sadece tek sahte uygulamanızı güncellemeniz gerekir ve tüm testleriniz kurulumlarını değiştirmeden buna karşı çalışır


2
Bu hala test edilen sınıfın çalıştığı yöntemler için verilerle bir sahte oluşturduğum duruma yol açıyor. Bu yüzden, uygulama değiştirildiğinde, testin kırılacağı sorunu hala var.
Michel

1
yine de demek istediğim senaryo için mükemmel olmasa da, yaklaşımımın neden daha iyi olduğunu açıklamak için düzenlendi
Ewan

1

Birim testleri genellikle beyaz kutu testleridir (gerçek koda erişiminiz vardır). Bu nedenle, içselleri bir dereceye kadar bilmek iyidir, ancak yeni başlayanlar için yapması daha kolaydır, çünkü iç davranışı test etmemelisiniz ("önce yöntemi çağırmak, sonra b ve sonra tekrar" gibi).

Sınıfınız (birim) söz konusu harici verilere (veya veri sağlayıcısına) bağlı olduğundan, veri sağlayan bir taklit enjeksiyonu tamam. Ancak, sonuçları doğrulamalısınız (sonuçlara ulaşmanın yolu değil)! Örneğin, bir Kişi örneği sağlar ve doğru e-posta adresine bir e-postanın gönderildiğini doğrularsınız, örneğin, e-posta için bir alay da sağlayarak, bu alaycı, daha sonradan testinize erişmek için muhatabın e-posta adresini depolamaktan başka bir şey yapmaz. -Kod. (Sanırım Martin Fowler onlara Mocks yerine Stubs diyor.)

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.