Özel yöntemleri nasıl test ediyorsunuz?


479

Bazı kamu ve özel yöntemleri olacak bir sınıf kütüphanesi inşa ediyorum. Özel yöntemleri birim olarak test edebilmek istiyorum (çoğunlukla gelişirken, aynı zamanda gelecekteki yeniden düzenleme için de yararlı olabilir).

Bunu yapmanın doğru yolu nedir?


3
Bir şeyi kaçırıyor olabilirim, ya da belki de sadece bu soru, pre-historicinternet yıllarında ... ama özel yöntemlerin birim testi artık hem kolay hem de basittir, Visual Studio gerektiğinde gerekli erişimci sınıflarını üretiyor ve test mantığının basit işlevsel testler için istenebileceğine yakın snippet'lerle doldurulması. Bkz. msdn.microsoft.com/tr-tr/library/ms184807%28VS.90%29.aspx
mjv

3
Bu stackoverflow.com/questions/34571/… 'in neredeyse bir kopyası gibi görünüyor .
Raedwald

Soru soran kişi görsel stüdyo kullanmıyor olabilir
Dave


Yanıtlar:


122

.Net kullanıyorsanız, InternalsVisibleToAttribute öğesini kullanmalısınız .


86
Yuck. Bu, serbest bırakılmış derlemelerinize derlenir.
Jay

14
@Jay - sürüm kodu için geçerli olmamasını sağlamak #if DEBUGiçin InternalsVisibleToöznitelik çevresinde kullanamazsınız ?
mpontillo

20
@Mike, yapabilirdiniz, ancak daha sonra kod testini değil, hata ayıklama kodunda birim testleri çalıştırabilirsiniz. Sürüm kodu optimize edildiğinden, farklı davranışlar ve farklı zamanlamalar görebilirsiniz. Çok iş parçacıklı kodda bu, birim testlerinizin yarış koşullarını uygun şekilde algılamayacağı anlamına gelir. Çok daha iyisi, @ AmazedSaint'in aşağıdaki önerisi aracılığıyla yansımayı kullanmak veya yerleşik PrivateObject / PrivateType kullanmaktır. Bu, test demetinizin tam güvenle çalıştığını varsayarak Sürüm sürümlerinde ayrıcalıkları görmenizi sağlar (MSTest yerel olarak çalışır)
Jay

121
Neyi kaçırıyorum? Özel yöntemlerin test edilmesine ilişkin özel soruya gerçekten cevap vermediğinde neden bu kabul edilen cevap olur? InternalsVisibleTo yalnızca OP olarak istendiği gibi özel olarak işaretlenmemiş yöntemleri (ve buraya inme nedenimi) göstermez. Sanırım Seven tarafından cevaplandırılan PrivateObject kullanmaya devam etmem gerekiyor mu?
Darren Lewis

6
Ben bunu biraz geç gelecek biliyorum @Jay, ama bir seçenek gibi bir şey kullanmaktır #if RELEASE_TESTcivarını InternalsVisibleToMike anlaşılacağı gibi, ve tanımlayıp senin Sürüm oluşturma yapılandırma bir kopyasını yapmak RELEASE_TEST. Sürüm kodunuzu optimizasyonlarla test edersiniz, ancak gerçekten sürüm için oluşturduğunuzda testleriniz atlanır.
Shaz

349

Özel bir yöntemi birim olarak test etmek istiyorsanız, bir şeyler yanlış olabilir. Birim testleri (genel olarak konuşursak), bir sınıfın arabirimini test etmek içindir, yani kamusal (ve korunan) yöntemler. Elbette bunun için bir çözümü "hackleyebilirsiniz" (sadece yöntemleri herkese açık hale getirseniz bile), ancak şunları da düşünebilirsiniz:

  1. Test etmek istediğiniz yöntem gerçekten test etmeye değerse, kendi sınıfına taşımanız faydalı olabilir.
  2. Özel yöntemin işlevini test ederek özel yöntemi çağıran genel yöntemlere daha fazla sınama ekleyin. (Yorumcuların belirttiği gibi, bunu yalnızca bu özel yöntemlerin işlevselliği gerçekten genel arabirimin bir parçasıysa yapmalısınız. Gerçekten kullanıcıdan gizlenen işlevleri (yani birim testi) gerçekleştiriyorlarsa, bu muhtemelen kötüdür).

38
Seçenek 2, birim testlerinin fonksiyonun altında yatan uygulama hakkında bilgi sahibi olmasını sağlar. Bunu yapmaktan hoşlanmıyorum. Genelde birim testlerin, uygulama hakkında hiçbir şey varsaymadan işlevi test etmesi gerektiğini düşünüyorum.
Herms

15
Uygulamanın test edilmesinin dezavantajları, uygulamada herhangi bir değişiklik yaparsanız, testlerin kırılması kırılgan olmasıdır. Yeniden düzenleme, TDD'deki testleri yazmak kadar önemli olduğu için bu istenmeyen bir durumdur.
JtR

30
Uygulamayı değiştirirseniz, testlerin kırılması gerekiyor . TDD ilk önce testleri değiştirmek anlamına gelir.
sleske

39
@sleske - Katılmıyorum. Eğer işlevselliği değişmedi testler gerçekten davranış / devlet değil, uygulamayı test edilmesi gerektiğinden, daha sonra testi kırmak için hiçbir sebep, yok. Testlerin kırılgan hale gelmesi jtr'ın anlamı budur. İdeal bir dünyada, yeniden düzenleme işleminizin sisteminizin işlevselliğini değiştirmediğini doğrulayarak, kodunuzu yeniden düzenleyebilmeniz ve testlerinizin geçmesini sağlayabilirsiniz.
Alconja

26
Naiflik için özür dilerim (henüz testle ilgili çok fazla deneyim yok), ancak her kod modülünü kendi başına test etmek için birim testi fikri değil mi? Özel yöntemlerin neden bu fikirden dışlanması gerektiğini gerçekten anlamıyorum.
OMill

118

Özel yöntemleri test etmek yararlı olmayabilir. Bununla birlikte, bazen test yöntemlerinden özel yöntemler çağırmayı da severim. Çoğu zaman test veri üretimi için kod çoğaltmasını önlemek için ...

Microsoft bunun için iki mekanizma sağlar:

Erişimciler

  • Sınıf tanımının kaynak koduna git
  • Sınıfın adını sağ tıklayın
  • "Özel Erişimci Oluştur" u seçin
  • Erişimcinin oluşturulması gereken projeyi seçin => Sonunda foo_accessor adında yeni bir sınıf bulacaksınız. Bu sınıf, derleme sırasında dinamik olarak oluşturulacak ve herkese açık olan tüm üyeleri özelleştirecektir.

Bununla birlikte, orijinal sınıfın arayüzündeki değişiklikler söz konusu olduğunda, mekanizma bazen biraz zorlanabilir. Çoğu zaman bunu kullanmaktan kaçınırım.

PrivateObject sınıfı Diğer yol Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject öğesini kullanmaktır

// Wrap an already existing instance
PrivateObject accessor = new PrivateObject( objectInstanceToBeWrapped );

// Retrieve a private field
MyReturnType accessiblePrivateField = (MyReturnType) accessor.GetField( "privateFieldName" );

// Call a private method
accessor.Invoke( "PrivateMethodName", new Object[] {/* ... */} );

2
Özel statik yöntemleri nasıl çağırıyorsunuz?
StuperUser


3
Özel Yöntemleri test etme erişimci yöntemi VS 2011'den itibaren kullanımdan kaldırılmaktadır. blogs.msdn.com/b/visualstudioalm/archive/2012/03/08/…
Misra

1
Burada Microsoft'un web sitesinde bulunan dokümanları okurken PrivateObject sınıfının kullanımdan kaldırıldığına dair herhangi bir açıklama görmüyorum. MSVS 2013 kullanıyorum ve beklendiği gibi çalışıyor.
stackunderflow

3
@RyanGates Başvurduğunuz sitedeki ilk çözüm, özel üyelere erişim çözümünün "Kodunuzdaki iç ve özel API'lere erişmeye yardımcı olmak için PrivateObject sınıfını kullanmak
stackunderflow

78

"Sadece dış arayüzü test etmekle ilgilenmelisiniz" felsefesine katılmıyorum. Bir araba tamirhanesinin sadece tekerleklerin dönüp dönmediğini görmek için testler yapması gerektiğini söylemek biraz benziyor. Evet, nihayetinde dış davranışla ilgileniyorum ama kendi, özel, iç testlerimin biraz daha spesifik ve konuya girmesini seviyorum. Evet, eğer refaktör olursam, bazı testleri değiştirmek zorunda kalabilirim, ancak büyük bir refactor olmadıkça, sadece birkaçını değiştirmek zorunda kalacağım ve diğer (değişmemiş) dahili testlerin hala çalıştığı gerçeği, yeniden düzenleme başarılı oldu.

Yalnızca dahili arabirimi kullanarak tüm dahili vakaları kapsamayı deneyebilirsiniz ve teorik olarak her dahili yöntemi (veya en azından önemli olan her birini) tamamen genel arabirimi kullanarak test etmek mümkündür, ancak bunu başarmak için kafanızda durmanız gerekebilir. bu ve ortak arayüz üzerinden yürütülen test senaryoları ile test etmek için tasarladıkları çözümün dahili kısmı arasındaki bağlantıyı fark etmek zor veya imkansız olabilir. İşaret edersek, iç makinenin düzgün çalışmasını garanti eden bireysel testler, yeniden düzenleme ile ortaya çıkan küçük test değişikliklerine değer - en azından benim deneyimim bu. Her yeniden düzenleme için testlerinizde büyük değişiklikler yapmanız gerekiyorsa, belki de bu mantıklı değildir, ancak bu durumda, belki de tasarımınızı tamamen yeniden düşünmelisiniz.


20
Korkarım hala sana katılmıyorum. Her bileşene bir kara kutu olarak işlem yapılması, modüllerin sorunsuz bir şekilde içeri / dışarı alınmasını sağlar. Yapmanız gereken bir FooServiceşey varsa X, tek yapmanız gereken X, talep edildiğinde gerçekten yapmaktır . Nasıl yaptığı önemli değil. Sınıfta arabirimden ayırt edilemeyen sorunlar varsa (olası değildir), hala geçerlidir FooService. Bir sorun varsa bir arayüz üzerinden görünür, kamu üyelerine bir test bunu algılamalıdır. Bütün mesele, tekerlek düzgün döndüğü sürece, tekerlek olarak kullanılabilmesi olmalıdır.
Temel

5
Genel bir yaklaşım, eğer dahili mantığınız birim test gerektirdiğini hissettiğiniz kadar karmaşıksa, belki de birim test edilebilen bir kamu arayüzü ile bir tür yardımcı sınıfa çıkarılması gerektiğidir. Daha sonra 'ebeveyn' sınıfınız bu yardımcıdan kolayca yararlanabilir ve herkes uygun şekilde birim test edilebilir.
Mir

9
@Temel: Bu cevapta tamamen yanlış mantık. Özel yönteme ihtiyacınız olduğunda klasik durum, genel yöntemlerle yeniden kullanılmak üzere bazı kodlara ihtiyaç duyduğunuz zamandır. Bu kodu bazı PrivMethod'a koydunuz. Bu yöntem halka açık olmamalıdır, ancak PrivMethod'a dayanan genel yöntemlerin gerçekten buna güvenebilmesini sağlamak için teste ihtiyaç duyar.
Dima

6
@Dima Kesinlikle bir sorun varsa PrivMethod, PubMethodhangi çağrıların PrivMethodaçığa çıkması gerektiğini gösteren bir test ? Para üstünü ne olur SimpleSmtpServicea GmailService? Birdenbire özel testleriniz, uygulama tasarlandığı gibi mükemmel bir şekilde çalışabilse bile, artık var olmayan veya belki de farklı çalışan ve başarısız olacak kodu işaret ediyor. Her iki e-posta gönderenine de uygulanacak karmaşık bir işlem varsa, belki de EmailProcessorher ikisinin de kullanabileceği ve ayrı olarak test edilebileceği bir işlem olmalıdır?
Temel

2
@miltonb Buna farklı gelişim tarzlarından bakabiliriz. İçindekileri WRT, onları birim test etme eğiliminde değilim. Bir sorun varsa (arayüz testleri ile tanımlandığı gibi), bir hata ayıklayıcı takarak izlemesi kolaydır veya sınıf çok karmaşıktır ve bölünmelidir (test edilen yeni sınıf biriminin genel arayüzü ile) IMHO
Basic

51

Nadir durumlarda özel işlevleri test etmek istedim, genellikle onları korunmak için değiştirdim ve genel sarma işlevine sahip bir alt sınıf yazdım.

Sınıf:

...

protected void APrivateFunction()
{
    ...
}

...

Test için alt sınıf:

...

[Test]
public void TestAPrivateFunction()
{
    APrivateFunction();
    //or whatever testing code you want here
}

...

1
Hatta o alt sınıfı gerçek sınıfı karıştırmak yerine birim test dosyanıza koyabilirsiniz. Kurnazlık için +1.
Tim Abell

Mümkünse her zaman testle ilgili kodu birim test projesine koydum. Bu sadece psuedo-koduydu.
Jason Jackson

2
Bu işlev özel değil, korunuyor, net sonuç ... kodunuzu daha az güvenli hale getirdiniz / çocuk türlerine özel işleve maruz bıraktınız
War

22

Daha temel bir soru sorulması gerektiğini düşünüyorum, neden özel yöntemi ilk başta test etmeye çalışıyorsunuz. Bu, özel yöntemi bu sınıfın genel arabirimi aracılığıyla test etmeye çalıştığınız bir kod kokusudur, oysa bu yöntem bir uygulama detayı olduğu için bir nedenden dolayı özeldir. Kişi, yalnızca kamusal arayüzün davranışı ile ilgilenmelidir.

Özel yöntemin davranışını ortak refactorings kullanarak test etmek isterseniz, kodunu başka bir sınıfa çıkarabilirim (belki paket düzeyinde görünürlükle, bu yüzden genel API'nin bir parçası olmadığından emin olun). Daha sonra davranışlarını ayrı ayrı test edebilirim.

Yeniden düzenleme ürünü, özel yöntemin artık orijinal sınıfa ortak çalışan ayrı bir sınıf olduğu anlamına gelir. Davranışı kendi birim testleri ile iyi anlaşılmış olacaktır.

Daha sonra orijinal sınıfı test etmeye çalıştığımda davranışını alay edebilirim, böylece daha sonra genel arayüzün birleşik patlamasını ve tüm özel yöntemlerinin davranışını test etmek yerine o sınıfın genel arayüzünün davranışını test etmeye konsantre olabilirim. .

Bunu araba sürmeye benzer görüyorum. Bir araba kullandığımda motor kaputu takılıyken araba kullanmıyorum, böylece motorun çalıştığını görebiliyorum. Aracın çalıştığı arayüze, yani motorun çalıştığını bilmek için devir sayacına ve hız göstergesine güveniyorum. Gaz pedalına bastığımda arabanın gerçekten hareket etmesine güveniyorum. Motoru test etmek istersem tek başına bununla ilgili kontroller yapabilirim. : D

Elbette eski bir uygulamanız varsa özel yöntemleri doğrudan test etmek son çare olabilir, ancak daha iyi testler sağlamak için eski kodun yeniden düzenlenmesi tercih edilir. Michael Feathers bu konuda harika bir kitap yazdı. http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052


16
Bu cevapta tamamen yanlış mantık. Özel yönteme ihtiyacınız olduğunda klasik durum, genel yöntemlerle yeniden kullanılmak üzere bazı kodlara ihtiyaç duyduğunuz zamandır. Bu kodu bazı PrivMethod'a koydunuz. Bu yöntem herkese açık olmamalıdır, ancak PrivMethod'a dayanan genel yöntemlerin gerçekten buna güvenebileceğinden emin olmak için test edilmesi gerekir.
Dima

İlk gelişim sırasında mantıklıdır, ancak standart regresyon elbisenizde özel yöntemler için testler ister misiniz? Eğer öyleyse, uygulama değişirse, test paketini bozabilir. OTOH, regresyon testleriniz yalnızca harici olarak görünen genel yöntemlere odaklanırsa, özel yöntem daha sonra kırılırsa, regresyon paketinin hatayı yine de algılaması gerekir. Daha sonra gerekirse eski özel testi tozdan alabilirsiniz.
Alex Blakemore

9
Katılmıyorum, sadece genel arayüzü test etmelisiniz, aksi halde neden özel yöntemlere ihtiyaç duyuluyor. Bu durumda hepsini herkese açık hale getirin ve hepsini test edin.Özel yöntemleri test ediyorsanız, düzenlemeyi kırıyorsunuz. Özel bir yöntemi test etmek istiyorsanız ve birden fazla genel yöntemde kullanılıyorsa, kendi sınıfına taşınmalı ve tek başına test edilmelidir.Tüm genel yöntemler daha sonra bu yeni sınıfa devredilmelidir. orijinal sınıfın 'arayüzü ile davranışın değişmediğini ve yetki verilen özel yöntem için ayrı testleriniz olduğunu doğrulayabilirsiniz.
Büyük Kahuna

@Big Kahuna - Özel yöntemleri test etmeniz gereken bir durum olmadığını düşünüyorsanız, yeterince büyük / karmaşık bir projeyle hiç çalışmadınız. Çoğu zaman, istemciye özgü doğrulama gibi genel bir işlev, kodu daha okunabilir hale getirmek için çok basit özel yöntemler çağıran 20 satırla sonuçlanır, ancak yine de her bir özel yöntemi test etmeniz gerekir. Genel fonksiyonun 20 katını test etmek, birim testleri başarısız olduğunda çıkış yapmayı çok zorlaştıracaktır.
Pedro.The.Kid

1
Bir FTSE 100 şirketinde çalışıyorum. Sanırım zamanımda birkaç karmaşık proje gördüm, teşekkürler. Bu seviyeye test etmeniz gerekiyorsa, ayrı ortak çalışanlar olarak her özel yöntem ayrı ayrı test edilmelidir, çünkü test edilmesi gereken bireysel davranışları olduğu anlamına gelir. Ana arabulucu nesnesi için test daha sonra bir etkileşim testi haline gelir. Sadece doğru stratejinin çağrıldığını test eder. Senaryonuz söz konusu sınıf SRP'yi takip etmiyor gibi geliyor. Değiştirmek için tek bir nedeni yok ama SRP ihlali 20 =>. GOOS kitabını veya Bob Amca'yı okuyun. YMWV
Big Kahuna

17

Özel türler, içler ve özel üyeler bir nedenden ötürü ve çoğu zaman onlarla doğrudan uğraşmak istemezsiniz. Ve eğer yaparsanız, şansınız daha sonra kırılacaksınız, çünkü bu meclisleri oluşturan adamların özel / dahili uygulamaları bu şekilde tutacağına dair bir garanti yoktur.

Ancak, zaman zaman, derlenmiş veya üçüncü taraf meclislerinde bazı hackler / keşifler yaparken, kendimi özel bir sınıf veya özel veya dahili bir kurucu ile bir sınıf başlatmak istiyorum. Ya da zaman zaman, değiştiremediğim önceden derlenmiş eski kütüphanelerle uğraşırken - sonunda özel bir yönteme karşı bazı testler yazıyorum.

Böylece AccessPrivateWrapper doğdu - http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html - C # 4.0 dinamik özelliklerini ve yansımasını kullanarak işi kolaylaştıracak hızlı bir sarmalayıcı sınıfıdır.

Gibi iç / özel türler oluşturabilirsiniz

    //Note that the wrapper is dynamic
    dynamic wrapper = AccessPrivateWrapper.FromType
        (typeof(SomeKnownClass).Assembly,"ClassWithPrivateConstructor");

    //Access the private members
    wrapper.PrivateMethodInPrivateClass();

12

Özel yöntemi iki şekilde test edebilirsiniz

  1. PrivateObjectsözdiziminin aşağıdaki gibi olduğu bir sınıf örneği oluşturabilirsiniz

    PrivateObject obj= new PrivateObject(PrivateClass);
    //now with this obj you can call the private method of PrivateCalss.
    obj.PrivateMethod("Parameters");
  2. Yansımayı kullanabilirsiniz.

    PrivateClass obj = new PrivateClass(); // Class containing private obj
    Type t = typeof(PrivateClass); 
    var x = t.InvokeMember("PrivateFunc", 
        BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public |  
            BindingFlags.Instance, null, obj, new object[] { 5 });

Güzel cevap, ancak # 1 için sözdiziminiz yanlış. PrivateClassİlk örneğini bildirmeniz ve kullanmanız gerekir. stackoverflow.com/questions/9122708/…
SharpC

10

Ayrıca InternalsVisibleToAttribute yöntemini kullandım. Bunu başarmak için daha önce özel yöntemlerinizi dahili yapmaktan rahatsızlık duyuyorsanız, belki de doğrudan ünite testlerinin konusu olmamalıdır.

Sonuçta, sınıfınızın davranışını belirli bir uygulamadan ziyade test ediyorsunuz - ikincisini değiştirmeden ikincisini değiştirebilirsiniz ve testleriniz yine de geçmelidir.


Uygulamadan ziyade davranışları test etme konusunu seviyorum. Birim testlerinizi uygulamaya (özel yöntemler) bağlarsanız, testler kırılgan hale gelir ve uygulama değiştiğinde değiştirilmesi gerekir.
Bob Horn

9

2 tür özel yöntem vardır. Statik Özel Yöntemler ve Statik Olmayan Özel yöntemler (Örnek Yöntemleri). Aşağıdaki 2 makale, özel yöntemlerin örneklerle nasıl test edileceğini açıklamaktadır.

  1. Statik Özel Yöntemlerde Birim Testi
  2. Statik Olmayan Özel Yöntemlerde Birim Testi

Bazı örnekler verin, sadece bir bağlantı
vermekle kalmayın

Çirkin gözüküyor. Fikri yok. MS'den kötü çözüm. Şoktayım !
alerya

En kolay yolu, özel statik yöntemleri test etmek
Grant

8

MS Test, VSCodeGenAccessors adlı bir dosya oluşturarak özel üyeleri ve yöntemleri projede kullanılabilir hale getiren yerleşik bir özelliğe sahiptir.

[System.Diagnostics.DebuggerStepThrough()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
    internal class BaseAccessor
    {

        protected Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject m_privateObject;

        protected BaseAccessor(object target, Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
        {
            m_privateObject = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject(target, type);
        }

        protected BaseAccessor(Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
            :
                this(null, type)
        {
        }

        internal virtual object Target
        {
            get
            {
                return m_privateObject.Target;
            }
        }

        public override string ToString()
        {
            return this.Target.ToString();
        }

        public override bool Equals(object obj)
        {
            if (typeof(BaseAccessor).IsInstanceOfType(obj))
            {
                obj = ((BaseAccessor)(obj)).Target;
            }
            return this.Target.Equals(obj);
        }

        public override int GetHashCode()
        {
            return this.Target.GetHashCode();
        }
    }

BaseAccessor'dan türetilen sınıflarla

gibi

[System.Diagnostics.DebuggerStepThrough()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
internal class SomeClassAccessor : BaseAccessor
{

    protected static Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType m_privateType = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType(typeof(global::Namespace.SomeClass));

    internal SomeClassAccessor(global::Namespace.Someclass target)
        : base(target, m_privateType)
    {
    }

    internal static string STATIC_STRING
    {
        get
        {
            string ret = ((string)(m_privateType.GetStaticField("STATIC_STRING")));
            return ret;
        }
        set
        {
            m_privateType.SetStaticField("STATIC_STRING", value);
        }
    }

    internal int memberVar    {
        get
        {
            int ret = ((int)(m_privateObject.GetField("memberVar")));
            return ret;
        }
        set
        {
            m_privateObject.SetField("memberVar", value);
        }
    }

    internal int PrivateMethodName(int paramName)
    {
        object[] args = new object[] {
            paramName};
        int ret = (int)(m_privateObject.Invoke("PrivateMethodName", new System.Type[] {
                typeof(int)}, args)));
        return ret;
    }

8
Gen'd dosyaları yalnızca VS2005'te bulunur. 2008 yılında perde arkasında üretilirler. Ve onlar bir iğrenç. Ve ilişkili Gölge görevi bir yapı sunucusunda kesintili.
Ruben Bartelink

Ayrıca VS2012-2013'te erişimciler kullanımdan kaldırıldı.
Zephan Schroeder

5

CodeProject üzerinde, özel yöntemleri test etmenin artılarını ve eksilerini kısaca tartışan bir makale vardır. Daha sonra özel yöntemlere erişmek için bazı yansıma kodları sağlar (Marcus'un yukarıda sağladığı koda benzer.) Örnekle bulduğum tek sorun, kodun aşırı yüklenmiş yöntemleri dikkate almamasıdır.

Makaleyi burada bulabilirsiniz:

http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx


4

Bunları bildirin internalve ardından InternalsVisibleToAttributebirim test düzeneğinizin onları görmesine izin vermek için düğmesini kullanın .


11
InternalsVisibleTo kullanmayı sevmiyorum çünkü yöntemi bir sebepten dolayı özel yaptım.
swilliams

4

Derleyici yönergeleri kullanma eğilimim yok çünkü işleri hızlı bir şekilde karıştırıyorlar. Gerçekten ihtiyacınız varsa hafifletmenin bir yolu onları kısmi bir sınıfa koymak ve üretim sürümünü yaparken derlemenizin bu .cs dosyasını yoksaymasını sağlamaktır.


Test erişimcilerini bir üretim sürümüne dahil edersiniz (derleyici optimizasyonlarını test etmek için), ancak bir sürümde hariç tutarsınız. Ama saçları ayırıyorum ve yine de iptal ettim çünkü bu şeyleri tek bir yere koymak iyi bir fikir. Fikir için teşekkürler.
CAD bloke

4

İlk önce kodunuzun özel yöntemlerini test etmemelisiniz. Derslerinizin genel öğeleri olan 'genel arayüz' veya API'yı test ediyor olmalısınız. API, dış arayanlara açıkta olduğunuz tüm genel yöntemlerdir.

Bunun nedeni, sınıfınızın özel yöntemlerini ve içsellerini test etmeye başladığınızda, sınıfınızın uygulanmasını (özel şeyler) testlerinize bağlamanızdır. Bu, uygulama ayrıntılarınızı değiştirmeye karar verdiğinizde testlerinizi de değiştirmeniz gerektiği anlamına gelir.

Bu nedenle InternalsVisibleToAtrribute kullanmaktan kaçınmalısınız.

Ian Cooper'ın bu konuyu kapsayan harika bir konuşması: Ian Cooper: TDD, hepsi nerede yanlış gitti?


3

Bazen, özel beyanları test etmek iyi olabilir. Temel olarak, bir derleyicinin yalnızca bir genel yöntemi vardır: Derleme (string outputFileName, params string [] sourceSFileNames). Her "gizli" bildirimi test etmeden böyle bir yöntemi test etmenin zor olacağını anladığınızdan eminim!

Bu yüzden daha kolay testler yapmak için Visual T #: oluşturduk. Bu ücretsiz bir .NET programlama dilidir (C # v2.0 uyumlu).

'.-' operatörü ekledik. Sadece '.' dışında, test edilen projenizdeki hiçbir şeyi değiştirmeden testlerinizdeki gizli bildirimlere de erişebilirsiniz.

Web sitesine bir göz atın: indirmek bunu ücretsiz .


3

Henüz kimsenin bunu söylemediğine şaşırdım, ancak kullandığım bir çözüm, kendini test etmek için sınıf içinde statik bir yöntem yapmaktır. Bu, test etmek için herkese açık ve özel her şeye erişmenizi sağlar.

Ayrıca, bir komut dosyası dilinde (Python, Ruby ve PHP gibi OO yetenekleri ile), çalıştırıldığında dosya testinin kendisini yapabilirsiniz. Değişikliklerin hiçbir şeyi bozmadığından emin olmanın güzel hızlı yolu. Bu açıkça tüm sınıflarınızı test etmek için ölçeklenebilir bir çözüm yapar: sadece hepsini çalıştırın. (bunu diğer dillerde de her zaman testlerini de yapan bir void main ile yapabilirsiniz).


1
Bu pratik olmasına rağmen, çok zarif değil. Bu, kod tabanından biraz karışıklık yaratabilir ve testlerinizi gerçek kodunuzdan ayırmanıza izin vermez. Harici olarak test etme yeteneği, statik yöntemleri elle yazmak yerine komut dosyası otomatik sınama komutunu açma yeteneğini açar.
Darren Reid

Bu, harici olarak test etmenizi engellemez ... İstediğiniz gibi statik yöntemi çağırmanız yeterlidir. Kod tabanı da dağınık değil ... yöntemi buna göre adlandırın. Ben "runTests" kullanıyorum, ama benzer bir şey işe yarıyor.
borunun,

Hakkınız, harici olarak test yapılmasını engellemez, ancak çok daha fazla kod üretir, yani kod tabanını dağınık hale getirir. Her sınıfın, bir veya daha fazla kurucusunda değişkenlerini başlatan bir çok özel yöntem olabilir. Test etmek için, test edilecek yöntemler olduğu kadar en az sayıda statik yöntem yazmak zorunda kalacaksınız ve doğru değerleri başlatmak için test yöntemlerinin büyük olması gerekebilir. Bu, kodun bakımını zorlaştıracaktır. Diğerlerinin söylediği gibi, bir sınıfın davranışını test etmek daha iyi bir yaklaşımdır, geri kalanı hata ayıklamak için yeterince küçük olmalıdır.
Darren Reid

Ben kimseyi test etmek için aynı sayıda satır kullanın (aslında daha sonra okuyacak daha az). TÜM özel yöntemlerinizi test etmek zorunda değilsiniz. Sadece test edilmesi gerekenler :) Ayrıca her birini ayrı bir yöntemle test etmeniz gerekmez. Bunu tek bir çağrı ile yapıyorum. Tüm sınıflarım tüm özel ve korumalı birim testlerini satır satır çalıştıran aynı şemsiye birimi test yöntemine sahip olduğundan, bu aslında LESS kodunun bakımını zorlaştırır. Tüm test koşum takımı tüm sınıflarımda aynı yöntemi çağırır ve bakım tüm sınıflarımda - testler ve hepsi içinde bulunur.
rube

3

Burada özel yöntemi test etmek istediğiniz herhangi bir sınıfta kullanabileceğiniz bir açık kod örneği oluşturmak istiyorum.

Test örneği sınıfınıza sadece bu yöntemleri ekleyin ve daha sonra belirtildiği şekilde kullanın.

  /**
   *
   * @var Class_name_of_class_you_want_to_test_private_methods_in
   * note: the actual class and the private variable to store the 
   * class instance in, should at least be different case so that
   * they do not get confused in the code.  Here the class name is
   * is upper case while the private instance variable is all lower
   * case
   */
  private $class_name_of_class_you_want_to_test_private_methods_in;

  /**
   * This uses reflection to be able to get private methods to test
   * @param $methodName
   * @return ReflectionMethod
   */
  protected static function getMethod($methodName) {
    $class = new ReflectionClass('Class_name_of_class_you_want_to_test_private_methods_in');
    $method = $class->getMethod($methodName);
    $method->setAccessible(true);
    return $method;
  }

  /**
   * Uses reflection class to call private methods and get return values.
   * @param $methodName
   * @param array $params
   * @return mixed
   *
   * usage:     $this->_callMethod('_someFunctionName', array(param1,param2,param3));
   *  {params are in
   *   order in which they appear in the function declaration}
   */
  protected function _callMethod($methodName, $params=array()) {
    $method = self::getMethod($methodName);
    return $method->invokeArgs($this->class_name_of_class_you_want_to_test_private_methods_in, $params);
  }

$ this -> _ callMethod ('_ someFunctionName', dizi (param1, param2, param3));

Parametreleri orijinal özel işlevde göründükleri sırayla verin


3

Tüm fess ve karışıklık olmadan özel yöntemler çalıştırmak isteyen herkes için. Bu, eski yansıma dışında hiçbir şey kullanmadan herhangi bir birim test çerçevesi ile çalışır.

public class ReflectionTools
{
    // If the class is non-static
    public static Object InvokePrivate(Object objectUnderTest, string method, params object[] args)
    {
        Type t = objectUnderTest.GetType();
        return t.InvokeMember(method,
            BindingFlags.InvokeMethod |
            BindingFlags.NonPublic |
            BindingFlags.Instance |
            BindingFlags.Static,
            null,
            objectUnderTest,
            args);
    }
    // if the class is static
    public static Object InvokePrivate(Type typeOfObjectUnderTest, string method, params object[] args)
    {
        MemberInfo[] members = typeOfObjectUnderTest.GetMembers(BindingFlags.NonPublic | BindingFlags.Static);
        foreach(var member in members)
        {
            if (member.Name == method)
            {
                return typeOfObjectUnderTest.InvokeMember(method, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, null, typeOfObjectUnderTest, args);
            }
        }
        return null;
    }
}

Daha sonra gerçek testlerinizde böyle bir şey yapabilirsiniz:

Assert.AreEqual( 
  ReflectionTools.InvokePrivate(
    typeof(StaticClassOfMethod), 
    "PrivateMethod"), 
  "Expected Result");

Assert.AreEqual( 
  ReflectionTools.InvokePrivate(
    new ClassOfMethod(), 
    "PrivateMethod"), 
  "Expected Result");

2

MbUnit, Reflektör olarak adlandırılan güzel bir paketleyici aldı.

Reflector dogReflector = new Reflector(new Dog());
dogReflector.Invoke("DreamAbout", DogDream.Food);

Ayrıca, özellikleri ayarlayabilir ve özelliklerden değerler alabilirsiniz

dogReflector.GetProperty("Age");

"Test özel" ile ilgili olarak .. mükemmel dünyada kabul ediyorum. özel birim testleri yapmanın bir anlamı yoktur. Ancak gerçek dünyada, kodu yeniden düzenlemek yerine özel testler yazmak isteyebilirsiniz.


4
Sadece bilgi için, Gallio / MbUnit v3.2'deki Reflectordaha güçlü ile değiştirildi Mirror. ( gallio.org/wiki/doku.php?id=mbunit:mirror )
Yann Trevin

2

Özel yöntemlerin birim testi hakkında iyi bir makale . Ancak, neyin daha iyi olduğundan emin değilim. Çoğumuz ikinci yolu seçeceğinden eminim.


2

Bence sadece sınıfınızın herkese açık API'sını birim test etmelisiniz.

Bir metodu herkese açık hale getirmek için, üniteyi test etmek amacıyla, uygulama ayrıntılarını ortaya koyan kapsüllemeyi keser.

İyi bir genel API, müşteri kodunun acil bir hedefini çözer ve bu hedefi tamamen çözer.


Bu doğru cevap IMO olmalıdır. Çok sayıda özel yönteminiz varsa, bunun nedeni büyük olasılıkla kendi ortak arayüzüne girmeniz gereken gizli bir sınıfınız olmasıdır.
17'de sunfred

2

Kullandığım PrivateObject sınıfı. Ancak daha önce de belirtildiği gibi özel yöntemleri test etmekten kaçınmak daha iyidir.

Class target = new Class();
PrivateObject obj = new PrivateObject(target);
var retVal = obj.Invoke("PrivateMethod");
Assert.AreEqual(retVal);

2
CC -Dprivate=public

"CC", kullandığım sistemdeki komut satırı derleyicisidir. -Dfoo=bareşdeğerini yapar #define foo bar. Böylece, bu derleme seçeneği tüm özel şeyleri etkin bir şekilde herkese açık olarak değiştirir.


2
Bu nedir? Bu Visual Studio için geçerli mi?
YeahStu

"CC", kullandığım sistemdeki komut satırı derleyicisidir. "-Dfoo = bar", "#define foo bar" ile eşdeğerdir. Böylece, bu derleme seçeneği tüm özel şeyleri etkin bir şekilde herkese açık olarak değiştirir. ha-ha!
Mark Harrison

Visual Studio'da oluşturma ortamınızda bir tanım ayarlayın.
Mark Harrison

1

İlk olarak yöntem imzası için bir örnek:

private string[] SplitInternal()
{
    return Regex.Matches(Format, @"([^/\[\]]|\[[^]]*\])+")
                        .Cast<Match>()
                        .Select(m => m.Value)
                        .Where(s => !string.IsNullOrEmpty(s))
                        .ToArray();
}

İşte test:

/// <summary>
///A test for SplitInternal
///</summary>
[TestMethod()]
[DeploymentItem("Git XmlLib vs2008.dll")]
public void SplitInternalTest()
{
    string path = "pair[path/to/@Key={0}]/Items/Item[Name={1}]/Date";
    object[] values = new object[] { 2, "Martin" };
    XPathString xp = new XPathString(path, values);

    PrivateObject param0 = new PrivateObject(xp);
    XPathString_Accessor target = new XPathString_Accessor(param0);
    string[] expected = new string[] {
        "pair[path/to/@Key={0}]",
        "Items",
        "Item[Name={1}]",
        "Date"
    };
    string[] actual;
    actual = target.SplitInternal();
    CollectionAssert.AreEqual(expected, actual);
}

1

Bunu yapmanın bir yolu, yönteminizin olması protectedve test edilecek sınıfınızı devralan bir test fikstürünün yazılmasıdır. Bu şekilde, yönteminizi değiştirmiyorsunuz public, ancak testi etkinleştiriyorsunuz.


Bunu kabul etmiyorum, çünkü tüketicilerinizin temel sınıftan miras almasına ve korumalı işlevleri kullanmasına da izin vereceksiniz. Bu, bu işlevleri özel veya dahili yaparak ilk etapta önlemek istediğiniz bir şeydi.
Nick N.

1

1) Eski bir kodunuz varsa, özel yöntemleri test etmenin tek yolu yansımadır.

2) Yeni bir kodsa, aşağıdaki seçeneklere sahipsiniz:

  • Yansımayı kullan (karmaşık)
  • Aynı sınıfta birim testi yazma (test kodunu da içinde tutarak üretim kodunu çirkin yapar)
  • Refactor ve yöntemi bir tür util sınıfında kamuya açık hale getirme
  • @VisibleForTesting ek açıklamasını kullanın ve özel bilgileri kaldırın

En basit ve en az karmaşık olan ek açıklama yöntemini tercih ederim. Tek sorun, büyük bir endişe olmadığını düşündüğüm görünürlüğü artırdık. Her zaman arabirime kodlama yapmalıyız, bu yüzden bir arabirim MyService ve bir uygulama MyServiceImpl varsa, MyServiceTest (test arabirimi yöntemleri) ve MyServiceImplTest (test özel yöntemleri) olan ilgili test sınıflarına sahip olabiliriz. Özel yöntemlerin görünürlüğü artırılsa bile, tüm istemciler arabirimi bu şekilde kullanmalıdır.


1

Ayrıca hata ayıklama Modunda oluştururken genel veya dahili (InternalsVisibleToAttribute ile) olarak bildirebilirsiniz:

    /// <summary>
    /// This Method is private.
    /// </summary>
#if DEBUG
    public
#else
    private
#endif
    static string MyPrivateMethod()
    {
        return "false";
    }

Kodu şişirir, ancak privatebir sürüm derlemesinde olacaktır.


0

Özel yöntem için sınama yöntemi Visual Studio 2008'den oluşturabilirsiniz. Özel bir yöntem için birim sınama oluşturduğunuzda, sınama projenize bir sınama başvurular klasörü eklenir ve bu klasöre bir erişimci eklenir. Erişimci, birim test yönteminin mantığında da belirtilir. Bu erişimci, birim testinizin test ettiğiniz koddaki özel yöntemleri çağırmasını sağlar. Ayrıntılar için

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


0

InternalsVisibleToAtrribute öğesinin, derlemenizin güçlü olarak adlandırılmış bir gereksinimi olduğunu unutmayın ; bu, daha önce bu gereksinimi olmayan bir çözümde çalışıyorsanız kendi sorun kümesini oluşturur. Özel yöntemleri test etmek için erişimciyi kullanıyorum. Bunun bir örneği için şu soruya bakın .


2
Hayır, InternalsVisibleToAttributeyok değil gerektiren montajlarınızın kesin adlandırılmış olması. Şu anda durum böyle olmayan bir projede kullanıyorum.
Cody Gray

1
Bunu açıklığa kavuşturmak için: "Hem mevcut derlemenin hem de arkadaş derlemesinin imzasız olması veya her ikisinin de güçlü bir adla imzalanması gerekir." - MSDN'den
Hari
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.