2009-09-02 107 views
5

假設我有一個類庫的項目。我在這個庫中有一個類,這個類只有一些在類內部使用的方法。像這樣:單元測試和對象的範圍 - 如何測試私人/內部方法等?

public class MyClass 
{ 
    public void MyPublicMethod 
    { 
    int k 

    // do something ... 

    int z = MyInternalMethod(k); 
    // do something else ... 

    } 

    internal int MyInternalMethod(int i) 
    { 
     // do something ... 

    } 
} 

現在我想爲這些方法編寫單元測試。我想創建一個「單元測試」項目,從它引用NUnit的,寫這樣的事情

[TestFixture] 
public class UnitTests 
{ 
    private MyClass myClass; 

    [SetUp] 
    public void SetupTest 
    { 
    myClass = new MyClass(); 
    } 

    [Test] 
    public void TestMyInternalMethod 
    { 
    int z = 100; 
    int k = myClass.MyInternalMethod(z); //CAN NOT DO THIS! 
    Assert.AreEqual(k, 100000); 
    } 

    [TearDown] 
    public void TearDown 
    { 
    myClass = null; 
    } 
} 

當然,我不能這樣做,因爲MyInternalMethod範圍。處理這種情況的正確方法是什麼?

回答

2

通過使用InternalsVisibleToAttribute,可以使內部對某些程序集可見。

+0

這是對我的情況最有效的答案,只需要添加一行 - 確實是一個寶石。有人downvoted它沒有說明原因,雖然... – Evgeny 2009-09-03 01:15:38

+0

我很好奇downvote了。以這種方式測試內部結構與其他方法有什麼不同嗎? – 2009-09-03 03:20:40

+2

鏈接已經死了 – 2013-01-17 16:36:01

-2

我喜歡把我的單元測試放在與他們測試的類相同的類中。這有兩個好處,第一個是它解決了你遇到的問題,第二個是你永遠不會失去或忘記他們,因爲如果他們是在一個單獨的程序集中經常會遇到這種情況。

並非所有人都同意這種方法(請參閱我之前提到的SO question),但至今我還沒有發現或有任何缺陷向我指出。我一直在做這樣的單元測試,現在已經有4年或5年了。

#if UNITTEST 
using NUnit.Framework; 
#endif 

public class MyBlackMagic 
{ 
    private int DoMagic() 
    { 
     return 1; 
    } 

    #if UNITTEST 

    [TestFixture] 
    public class MyBlackMagicUnitTest 
    { 
     [TestFixtureSetUp] 
     public void Init() 
     { 
      log4net.Config.BasicConfigurator.Configure(); 
     } 

     [Test] 
     public void DoMagicTest() 
     { 
      Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name); 
      Assert.IsTrue(DoMagic() == 1, "You are not a real magician!"); 
     } 
    } 

    #endif 
} 
+2

壞,壞,壞 - 請不要這樣做。 – Corehpf 2009-09-02 00:42:48

+1

但是,您需要從當時的任何項目中引用nunit.framework。我不確定這是否會帶來更長的編譯時間,更大的可執行文件,但這看起來有點笨拙。 – Evgeny 2009-09-02 00:47:11

+0

@Corehpf - 我們不是在這裏討論宗教。如果你有反對的東西提供它的論據。 – sipwiz 2009-09-02 00:52:02

4

猜猜這取決於你的想法是什麼單位是吧?我通常爲可訪問接口編寫單元測試,並忽略私有內容。我已經和那些會爲單元測試訪問保護私有事物(java)的人一起工作。我真的不喜歡這種方法,因爲它犧牲了測試訪問的類設計的清潔。

+1

同意。我有時會使用InternalsVisibleTo,通常我可以讓某些類在內部但仍然測試它們。儘管如此,我不會捍衛這種良好做法 - 一般來說,測試公共接口。它使得重構變得更容易(減少你的測試中斷的機率)。 – TrueWill 2009-09-02 01:38:32

2

這裏有一個關於該主題的好文章:

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

我個人只是避免寫任何私人的方法是做任何事情非常複雜。還有其他一些方法可以封裝你不想公開的行爲,同時還可以讓你測試應該隱藏的東西。我認爲在完美封裝和可測試性之間有一個折衷。完美的封裝很難實現,而且讓自己更深入地瞭解類,通常會更有益處。這可能是有爭議的。

0

Visual Studio可以爲您生成私有訪問器。在MSDN上檢出Unit Tests for Private, Internal, and Friend Methods。我相信VS2005只是生成並添加私人訪問器類到您的單元測試項目。所以當事情改變時你必須重新生成它們。但是,VS2008會生成一個私有訪問程序集並使其可用於單元測試項目。你正在使用NUnit,但我認爲它應該沒問題。一探究竟。通過這種方式,您可以使您的實際代碼免受任何與測試相關的代碼和/或黑客入侵。

0

在過去,我已經在與測試類相同的名稱空間和程序集中創建了測試夾具來測試內部方法。我不是說內部方法是否應該被測試。實際上,你可能會測試它們,然後重構。

我還創建了部分類來測試私有方法,並在整個部分(這是它自己的文件)中使用了一個編譯器指令。同樣,不要說這是最好的,但有時你需要前進。

在構建時,我們可以在調試或發佈模式下運行單元測試,如果需要,我們可以從任一版本中去除測試代碼,因此在測試代碼中包含測試代碼時不會造成任何損害;如果有的話,它的參數與代碼和數據在一起=對象或對象和文檔註釋=文檔對象類似。換句話說:代碼和數據和測試和文檔評論聚在一起=內聚單元。

構建期間的額外時間可以忽略不計。

3

我只是測試公共方法(不,我不關心覆蓋範圍指標,我關心工作的功能)。

請注意,如果公共方法不使用內部方法,那麼內部方法不需要存在!

1

很多人會說你不應該測試內部方法,而是通過公共API測試它們。無論如何,如果你真的想訪問這些私人會員,你可以使用反思。

0

我已經使用了幾種方法來做到這一點。我已經使我的私有方法受到保護,以便我可以在單元測試中從類繼承,並創建一個「助手」類來測試這些方法。另一個是反思。反思是更容易海事組織,但確實違背了你的類應該如何設計測試。這是我正在談論的簡化版本。

public static class ReflectionHelper 
{ 
    public static object RunStaticMethod<TInstance>(string methodName, params object[] methodParams) 
    { 
     var methodType = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; 
     return RunMethod<TInstance>(null, methodName, methodType, methodParams); 
    } 

    public static object RunInstanceMethod<TInstance>(this TInstance instance, string methodName, params object[] methodParams) 
    { 
     var methodType = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; 
     return RunMethod<TInstance>(instance, methodName, methodType, methodParams); 
    } 

    private static object RunMethod<TInstance>(object instance, string methodName, BindingFlags methodType, params object[] methodParams) 
    { 
     var instanceType = typeof(TInstance); 
     var method = instanceType.GetMethod(methodName, methodType); 
     if (method == null) 
     { 
      throw new ArgumentException(string.Format("There is no method '{0}' for type '{1}'.", methodName, instanceType)); 
     } 

     var result = method.Invoke(instance, methodParams); 

     return result; 
    } 
} 

鑑於一類像這樣

public class User 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 

    internal string GetPrettyName() 
    { 
     return string.Concat(FirstName, " ", LastName); 
    } 

    static internal int GetSystemId(string userName) 
    { 
     // some magic here 
     return 13; 
    } 
} 

你會使用這些作爲使

var user = new User { FirstName = "Peter", LastName = "Gibbons" }; 

var name = user.RunInstanceMethod("GetPrettyName"); 
Assert.That(name, Is.EqualTo("Peter Gibbons")); 

var id = ReflectionHelper.RunStaticMethod<User>("GetSystemId", "tester"); 
Assert.That(id, Is.EqualTo(13)); 
1

有兩種情況:要麼你的私人法會從一些公共的方法調用,哪種情況下你可以通過這種方法來測試它們。或者,他們不會被某些公共方法調用,他們根本無法調用,是死代碼,應該刪除,而不是測試。

請注意,如果您正在進行TDD,私有方法只能通過從公共方法中提取出來,然後才能自動進行測試。