Özel yöntemleri NUnit ile nasıl test edersiniz?


104

NUnit'i nasıl doğru kullanacağımı merak ediyorum. İlk olarak, ana projemi referans olarak kullanan ayrı bir test projesi oluşturdum. Ancak bu durumda, özel yöntemleri test edemiyorum. Tahminim, test kodumu ana koduma eklemem gerektiğiydi ?! - Bunu yapmanın doğru yolu bu değil gibi görünüyor. (İçinde test bulunan nakliye kodu fikrinden hoşlanmıyorum.)

Özel yöntemleri NUnit ile nasıl test edersiniz?

Yanıtlar:


74

Genel olarak, birim testi, sonuçlar müşterinin bakış açısından doğru olduğu sürece uygulamanın önemsiz olduğu teorisi üzerine bir sınıfın genel arayüzüne hitap eder.

Dolayısıyla, NUnit halka açık olmayan üyeleri test etmek için herhangi bir mekanizma sağlamaz.


1
+1 Bu sorunla yeni karşılaştım ve benim durumumda özel ve genel anlam arasında gerçekleşen bir "haritalama" algoritması var, eğer kamuyu Birim Testi yapsaydım, aslında bir entegrasyon testi olurdu. Bu senaryoda test noktalarını koddaki bir tasarım problemine yazmaya çalıştığını düşünüyorum. bu durumda muhtemelen o "özel" yöntemi uygulayan farklı bir sınıf oluşturmalıyım.
andy

140
Bu argümana tamamen katılmıyorum. Birim testi sınıfla ilgili değil, kodla ilgili . Bir genel yöntemi olan bir sınıfım olsaydı ve kamusal yöntemin sonucunu oluşturmak için kullanılan on özel yöntem varsa, her özel yöntemin amaçlanan şekilde çalışmasını sağlamanın hiçbir yolu yoktur. Herkese açık yöntemde, doğru özel yöntemi doğru şekilde vuran test senaryoları oluşturmaya çalışabilirim. Ancak, public yöntemin asla değişmeyeceğine güvenmediğim sürece, özel yöntemin gerçekten çağrılıp çağrılmadığına dair hiçbir fikrim yok. Özel yöntemlerin, yazara değil, kodun üretim kullanıcılarına özel olması amaçlanır.
Quango

3
@Quango, standart bir test düzeneğinde "yazar" , kodun üretim kullanıcısıdır. Yine de, bir tasarım probleminin kokusunu almıyorsanız, sınıfı değiştirmeden genel olmayan yöntemleri test etme seçenekleriniz vardır. System.Reflectionhalka açık olmayan yöntemlere bağlama bayraklarını kullanarak erişmenize ve bunları çağırmanıza olanak tanır, böylece NUnit'i hackleyebilir veya kendi çerçevenizi kurabilirsiniz. Veya (daha kolay, sanırım), erişim değiştiricilerini değiştirmek için bir derleme zamanı bayrağı (#if TESTING) oluşturabilir ve mevcut çerçeveleri kullanmanıza izin verebilirsiniz.
harpo

23
Özel bir yöntem, bir uygulama ayrıntısıdır. Birim testlere sahip olmanın pek çok amacı , davranışı değiştirmeden uygulamanın yeniden düzenlenmesine izin vermektir . Özel yöntemler o kadar karmaşık hale gelirse, biri onları tek tek testlerle kaplamak isterse, bu lemma kullanılabilir: "Özel bir yöntem her zaman ayrı bir sınıfın genel (veya dahili) bir yöntemi olabilir". Yani bir mantık parçası teste tabi tutulacak kadar gelişmişse, muhtemelen kendi testleri ile kendi sınıfı olmaya adaydır.
Anders Forsgren

6
@AndersForsgren Ancak, entegrasyon veya fonksiyonel testin aksine birim testinin amacı, tam olarak bu tür ayrıntıları test etmektir. Ve kod kapsamı önemli bir birim testi ölçütü olarak kabul edilir, bu nedenle teoride her mantık parçası "teste tabi tutulacak kadar gelişmiş" tir.
Kyle Strand

57

Birim testinin odağının genel arayüz olması gerektiğini kabul etsem de, özel yöntemleri de test ederseniz kodunuz hakkında çok daha ayrıntılı bir izlenim edinirsiniz. MS test çerçevesi, PrivateObject ve PrivateType kullanımıyla buna izin verir, NUnit bunu yapmaz. Bunun yerine yaptığım şey:

private MethodInfo GetMethod(string methodName)
{
    if (string.IsNullOrWhiteSpace(methodName))
        Assert.Fail("methodName cannot be null or whitespace");

    var method = this.objectUnderTest.GetType()
        .GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);

    if (method == null)
        Assert.Fail(string.Format("{0} method not found", methodName));

    return method;
}

Bu, test edilebilirlik için kapsüllemeden ödün vermek zorunda kalmayacağınız anlamına gelir. Özel statik yöntemleri test etmek istiyorsanız, BindingFlag'lerinizi değiştirmeniz gerekeceğini unutmayın. Yukarıdaki örnek sadece örnek yöntemler içindir.


7
Bu küçük yardımcı yöntemlerin test edilmesi, birim testinde birim parçasını yakalar. Hepsi de hizmet sınıfları için uygun değildir.
Johan Larsson

12
Bu tam olarak ihtiyacım olan şey. Teşekkürler! Tek ihtiyacım olan, özel bir alanın düzgün şekilde kurulup kurulmadığını kontrol etmekti ve bunu genel arayüz aracılığıyla nasıl belirleyeceğimi bulmak için 3 saat harcamam gerekmiyordu. Bu, bir hevesle yeniden düzenlenemeyen mevcut koddur. Basit bir sorunun idealist dogma ile yanıtlanması çok komik ...
Droj

5
Soruyu gerçekten cevaplayan tek cevap olduğu için bu seçilen cevap olmalıdır.
bavaza

6
Kabul. Seçilen cevabın faydaları vardır, ancak OP'nin sorusu değil, "özel yöntemleri test etmeli miyim?" Sorusunun cevabıdır.
chesterbr

2
işe yarıyor. Teşekkürler! Bu, birçok insanın aradığı şey. Seçilen cevap bu olmalı,
Able Johnson

47

Birim testleri yazmak için yaygın bir model, yalnızca halka açık yöntemleri test etmektir.

Test etmek istediğiniz birçok özel yönteminiz olduğunu fark ederseniz, normalde bu kodunuzu yeniden düzenlemeniz gerektiğinin bir işaretidir.

Bu yöntemleri şu anda yaşadıkları sınıfta halka açık hale getirmek yanlış olur. Bu, o sınıfın sahip olmasını istediğiniz sözleşmeyi bozar.

Bunları yardımcı bir sınıfa taşımak ve orada halka açık hale getirmek doğru olabilir. Bu sınıf, API'niz tarafından ifşa edilmeyebilir.

Bu şekilde test kodu asla genel kodunuzla karıştırılmaz.

Benzer bir sorun, özel sınıfları yani. Montajınızdan dışa aktarmadığınız sınıflar. Bu durumda, InternalsVisibleTo özniteliğini kullanarak test kodu derlemenizi açıkça üretim kodu derlemesinin bir arkadaşı yapabilirsiniz.


1
+ 1 evet! Testlerimi yazarken bu sonuca vardım, iyi tavsiye!
andy

1
Test etmek istediğiniz, ancak API kullanıcılarına maruz bırakmadığınız özel yöntemleri, açığa çıkmayan ve onları herkese açık hale getiren farklı bir sınıfa taşımak tam da aradığım şeydi.
Thomas N

teşekkürler @morechilli. InternalsVisibleTo'yu daha önce hiç görmemiştim. Testi çok daha kolay hale getirdi.
Andrew MacNaughton

Selam. Yakın zamanda yorumsuz bazı olumsuz oylar aldı. Lütfen düşüncelerinizi veya endişelerinizi açıklamak için biraz geri bildirim sağlayın.
morechilli

6
Dolayısıyla, bir sınıf içinde birden çok yerde çağrılan ve yalnızca o sınıfın ihtiyaç duyduğu çok özel bir şeyi yapmak için özel bir yönteminiz varsa ve bu, genel API'nin bir parçası olarak ifşa edilmemelidir ... sınıf sadece test etmek için? Bunu sınıf için herkese açık hale getirseniz iyi olur.
Daniel Macias

21

Test derlemenizi, test ettiğiniz hedef derlemenin bir arkadaş derlemesi olarak bildirerek özel yöntemleri test etmek mümkündür. Ayrıntılar için aşağıdaki bağlantıya bakın:

http://msdn.microsoft.com/en-us/library/0tke9fxk.aspx

Bu, test kodunuzu çoğunlukla üretim kodunuzdan ayırdığı için yararlı olabilir. Hiç ihtiyaç duymadığım için bu yöntemi kendim hiç kullanmadım. Sanırım bunu, kodunuzun nasıl işlediğini görmek için test ortamınızda kopyalayamayacağınız aşırı test durumlarını denemek ve test etmek için kullanabilirsiniz.

Yine de söylendiği gibi, gerçekten özel yöntemleri test etmenize gerek yok. Kodunuzu daha küçük yapı taşlarına dönüştürmek istediğinizden daha çok istersiniz. Yeniden düzenlemeye geldiğinizde size yardımcı olabilecek bir ipucu, sisteminizin ilgili olduğu alanı denemek ve düşünmek ve bu alanda yaşayan 'gerçek' nesneler hakkında düşünmektir. Sisteminizdeki nesneleriniz / sınıflarınız, nesnenin içermesi gereken tam davranışı izole etmenize ve ayrıca nesnelerin sorumluluklarını sınırlamanıza izin verecek gerçek bir nesneyle doğrudan ilişkili olmalıdır. Bu, yalnızca belirli bir yöntemi test etmeyi mümkün kılmak yerine mantıksal olarak yeniden düzenleme yaptığınız anlamına gelir; nesnelerin davranışını test edebileceksiniz.

Hala dahili olarak test etme ihtiyacı hissediyorsanız, bir kod parçasına odaklanmak isteyebileceğinizden testinizde alay etmeyi de düşünebilirsiniz. Alay, bir nesneye bağımlılık enjekte ettiğiniz yerdir, ancak enjekte edilen nesneler 'gerçek' veya üretim nesneleri değildir. Davranış hatalarını izole etmeyi kolaylaştırmak için sabit kodlanmış davranışa sahip sahte nesnelerdir. Rhino.Mocks, esasen sizin için nesneleri yazacak popüler bir ücretsiz alay etme çerçevesidir. TypeMock.NET (topluluk sürümünün mevcut olduğu ticari bir ürün), CLR nesnelerini taklit edebilen daha güçlü bir çerçevedir. Örneğin bir veritabanı uygulamasını test ederken SqlConnection / SqlCommand ve Datatable sınıflarını alay etmek için çok kullanışlıdır.

Umarım bu yanıt, genel olarak Birim Testi hakkında sizi bilgilendirmek ve Birim Testinden daha iyi sonuçlar almanıza yardımcı olmak için size biraz daha fazla bilgi verecektir.


12
Özel değil (NUnit ile) dahili yöntemleri test etmek mümkündür .
TrueWill

6

Özel yöntemleri test etme yeteneğine sahip olmaktan yanayım. XUnit başladığında, kod yazıldıktan sonra işlevselliği test etmek için tasarlanmıştı. Arayüzün test edilmesi bu amaç için yeterlidir.

Birim testi, test odaklı geliştirmeye dönüşmüştür. Tüm yöntemleri test etme yeteneğine sahip olmak, bu uygulama için yararlıdır.


5

Bu soru ileri yıllarında, ancak bunu yapma tarzımı paylaşacağımı düşündüm.

Temel olarak, test ettikleri derlemedeki tüm birim test sınıflarım, o derleme için "varsayılan" ın altında bir "UnitTest" ad alanında var - her test dosyası bir:

#if DEBUG

...test code...

#endif

blok, ve tüm bunlar a) bir sürümde dağıtılmadığı ve b) çember atlama olmadan internal/ Friendlevel bildirimlerini kullanabileceğim anlamına geliyor .

Bunun sunduğu diğer bir şey, bu soruya daha uygun olarak, partialözel yöntemleri test etmek için bir proxy oluşturmak için kullanılabilen sınıfların kullanılmasıdır, bu nedenle, örneğin bir tamsayı değeri döndüren özel bir yöntem gibi bir şeyi test etmek için:

public partial class TheClassBeingTested
{
    private int TheMethodToBeTested() { return -1; }
}

montajın ana sınıflarında ve test sınıfında:

#if DEBUG

using NUnit.Framework;

public partial class TheClassBeingTested
{
    internal int NUnit_TheMethodToBeTested()
    {
        return TheMethodToBeTested();
    }
}

[TestFixture]
public class ClassTests
{
    [Test]
    public void TestMethod()
    {
        var tc = new TheClassBeingTested();
        Assert.That(tc.NUnit_TheMethodToBeTested(), Is.EqualTo(-1));
    }
}

#endif

Açıkçası, geliştirme sırasında bu yöntemi kullanmadığınızdan emin olmanız gerekir, ancak bir Yayın yapısı yakında bunu yaparsanız yanlışlıkla ona yapılan bir çağrıyı gösterecektir.


4

Birim testinin temel amacı, bir sınıfın genel yöntemlerini test etmektir. Bu halka açık yöntemler bu özel yöntemleri kullanacaktır. Birim testi, halka açık olanın davranışını test edecektir.


3

Bu soruya yanıt vermiyorsa özür dileriz, ancak yansıma kullanmak, #if #endif ifadeleri veya özel yöntemleri görünür kılmak gibi çözümler sorunu çözmez. Özel yöntemleri görünür kılmamanın birkaç nedeni olabilir ... peki ya üretim koduysa ve takım geriye dönük olarak birim testleri yazıyorsa.

Yalnızca MSTest (ne yazık ki) üzerinde çalıştığım proje için, özel yöntemleri test etmek için erişimcileri kullanarak bir yol var gibi görünüyor.


2

Özel işlevleri test etmiyorsunuz. Özel yöntemlere ve özelliklere girmek için yansımayı kullanmanın yolları vardır. Ancak bu gerçekten kolay değil ve bu uygulamayı kesinlikle önermiyorum.

Herkese açık olmayan hiçbir şeyi test etmemelisiniz.

Bazı dahili yöntemleriniz ve özellikleriniz varsa, bunu herkese açık hale getirmeyi veya testlerinizi uygulamayla birlikte göndermeyi düşünmelisiniz (gerçekten bir sorun olarak görmediğim bir şey).

Müşteriniz bir Test Paketi çalıştırabiliyorsa ve teslim ettiğiniz kodun aslında "çalışıyor" olduğunu görürse, bunu bir sorun olarak görmüyorum (IP'nizi bu yolla vermediğiniz sürece). Her sürüme dahil ettiğim şeyler test raporları ve kod kapsamı raporlarıdır.


Bazı müşteriler bütçeyi küçük tutmaya ve testlerin kapsamı hakkında tartışmalar başlatmaya çalışıyor. Bu insanlara test yazmanın kötü bir kodlayıcı olduğunuz ve kendi becerilerinize güvenmediğiniz için olmadığını açıklamak zor.
MrFox

1

Birim Testi teorisinde sadece sözleşme test edilmelidir. yani sadece sınıfın genel üyeleri. Ancak uygulamada geliştirici genellikle dahili üyeleri test etmek ister. - ve fena değil. Evet, teoriye aykırı, ancak pratikte bazen faydalı olabilir.

Dolayısıyla, gerçekten dahili üyeleri test etmek istiyorsanız, şu yaklaşımlardan birini kullanabilirsiniz:

  1. Üyenizi herkese açık yapın. Birçok kitapta yazarlar bu yaklaşımı basit
  2. Sen üyeleri dahili yapmak ve ekleyebilir InternalVisibleTo assebly için
  3. Sınıf üyelerini korumalı hale getirebilir ve test sınıfınızı test edilen sınıfınızdan devralabilirsiniz.

Kod örneği (sözde kod):

public class SomeClass
{
    protected int SomeMethod() {}
}
[TestFixture]
public class TestClass : SomeClass{

    protected void SomeMethod2() {}
    [Test]
    public void SomeMethodTest() { SomeMethod2(); }
}

Hamamböceği örnek kodunuzun içinde saklı görünüyor;) NUnit'in fikstürünün içindeki yöntem halka açık olmalıdır, aksi takdirde elde edersiniz Message: Method is not public.
Gucu112

@ Gucu112 Teşekkürler. Onardım. Genel olarak, amaç tasarım bakış açısından yaklaşımı göstermek olduğundan önemli değildir.
burzhuy

1

Yöntemlerinizi dahili olarak korumalı hale getirebilir ve ardından assembly: InternalsVisibleTo("NAMESPACE") test ad alanınızda kullanabilirsiniz.

Dolayısıyla HAYIR! Özel yöntemlere erişemezsiniz, ancak bu bir çözümdür.


0

Özel yöntemler paketini görünür yapardım. Bu şekilde, bu yöntemleri test edebilmeye devam ederken onu makul ölçüde gizli tutarsınız. Herkese açık arayüzlerin test edilmesi gereken tek şey olduğunu söyleyenlere katılmıyorum. Özel yöntemlerde, yalnızca dış arabirimler üzerinden doğru bir şekilde test edilemeyen gerçekten kritik kodlar vardır.

Dolayısıyla, doğru kod veya bilgi gizlemeye daha fazla önem verip vermemeniz gerçekten azalır. Paket görünürlüğünün iyi bir uzlaşma olduğunu söyleyebilirim, çünkü bu yönteme erişmek için birisinin sınıfını paketinize yerleştirmesi gerekir. Bu, bunun gerçekten akıllıca bir şey olup olmadığı konusunda onları iki kez düşünmelerini sağlamalı.

Ben bir Java btw adamıyım, bu yüzden paket görünürlüğü C # 'ta tamamen farklı bir şey olarak adlandırılabilir. Bu yöntemlere erişmek için iki sınıfın aynı isim alanında olması gerektiğini söylemek yeterli.

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.