2011-06-03 84 views
3

於是我沿着這我應該如何測試訪問私有成員的公共方法

private List<ConcurrentQueue<int>> listOfQueues = new List<ConcurrentQueue<int>>() 
public void InsertInt(int id, int value) 
{ 
    listOfQueues[id].Enqueue(value); 
} 

線的東西這是不是我不應該是單元測試?
如果是我如何測試InsertInt方法而不使用任何其他方法? 是否可以使用該方法從隊列中檢索數據以測試它是否正確輸入? 這是我應該使用模擬的東西嗎?

回答

2

你一般不想測試私人成員。單元測試的重點在於,從外部開始,課程將採用指定的輸入,並且(在需要時)將爲您提供正確的輸出。您不關心如何爲您提供這些輸出的實現,您只是在意它爲您提供正確的外部使用輸出。

爲了表明這一點,假設您創建了一個單元測試,驗證您的InsertInt()方法將int插入到listOfQueues。然後您的需求會發生變化,您必須更改實施策略,而不是使用List<ConcurrentQueue<int>>,它會變成Dictionary<string, ConcurrentQueue<int>>。這實際上可能不需要對輸入進行更改,並且您的輸出仍然可以通過任何輸出驗證,但是您的單元測試將失敗,因爲它被硬編碼爲實施。

更好的辦法是進行單元測試,確保如果用正確的輸入調用InsertInt(),您的輸出方法仍然會返回已更正的輸出,並且創建使用無效參數調用InsertInt()的單元測試會導致異常。然後,您可以更改有關您內部實施的所有內容,並確信您的班級仍能正常工作。單元測試的實施增加了額外的開銷,同時在可測試性方面提供了很少的好處。

請注意,我並不是說這個方法不應該被單元測試,它只是單元測試需要以反映外部對象如何與你的類交互的方式來開發。

+0

所以它更好在一個單元測試中測試多個東西,而不是將單元測試與實現聯繫起來? – scott 2011-06-03 13:05:41

+0

是的,我相信是這樣,因爲它模仿了真實世界中如何使用這個類。如果你獨立地測試這兩種方法,你就沒有一個單元測試來確保輸入和輸出方法實際上在一起工作,這只是一個假設。然後,您可以更改其中一個的實現(和測試),而您的單元測試將通過,但代碼將在生產中失敗。你仍然需要進行單元測試,如果你用x參數調用輸入方法,你的輸出會給你y個參數,因此私人測試是浪費時間和精力。 – KallDrexx 2011-06-03 13:12:19

+0

此外,它仍然是單元測試,因爲你的單元測試只處理一個類本身而不是在類之外,所以處理多種方法並不是一件壞事。 – KallDrexx 2011-06-03 13:13:14

1

由於此方法是一種公共方法,因此需要對其進行單元測試。

快速查看一下你的方法會發現,通過-1作爲id的值會導致ArgumentOutOfRangeException。如果已經設計了此方法的單元測試用例(假設存在許多此類方法),那麼在編碼期間就會實現這一點。

要檢查插入是否成功,可以使用@Oskar Kjellin指出的方法。

如果你想弄髒,那麼你可以使用Reflection來檢查值是否已經被插入。

// Sample with Queue instead of ConcurrentQueue 
private void TestInsertInt(int id, int value) 
{ 
    myInstance.InsertInt(id, value); 

    FieldInfo field = myInstance.GetType().GetField("listOfQueues", BindingFlags.NonPublic | BindingFlags.Instance); 
    List<Queue<int>> listOfQueues = field.GetValue(myInstance) as List<Queue<int>>; 
    int lastInserted = listOfQueues[id].Last(); 

    Assert.AreEqual(lastInserted, value); 
} 
+0

這就是我的感受。所以我想更重要的問題是做這件事的正確方法是什麼? – scott 2011-06-03 12:42:03

+0

你不需要單元測試私人成員來創建一個單元測試,傳遞一個零的ID將導致一個'ArgumentOutOfRangeException' – KallDrexx 2011-06-03 12:47:39

2

是的,你應該單元測試它。您可以使用私人訪問器來獲取listOfQueues。 您必須確保使用單元測試,該方法的行爲與例外情況相同,並且確實插入了該項目。

看看這篇文章對如何單元測試的私有方法http://msdn.microsoft.com/en-us/library/ms184807(v=vs.80).aspx

+0

以下文章列出將導致Visual Studio詢問你是否想添加一個私人accesser到部件。您可以通過將以下行添加到您的AssemblyInfo.cs頁面[assembly:System.Runtime.CompilerServices.InternalsVisibleTo(「」)]手動執行此操作。 – JMcCarty 2011-06-03 12:44:51

+0

InternalsVisibleTo僅使內部可見 - 不是私有的?上面的文章生成私有方法的反射包裝,然後您可以將其編譯到您的測試程序集中。 – 2011-06-03 12:47:02

+0

有沒有辦法爲會員生成私有訪問器而不是方法? – scott 2011-06-03 13:04:30

0

這是不是我不應該是單元測試 ?

如果你的目標100%的測試覆蓋率則是

如果是我怎麼測試,而無需使用任何其他 方法InsertInt 方法?

有幾個選項:

  • 使用不同的訪問方法, 你的建議。
  • 使容器內,並給予單元測試組件訪問內部構件
  • 因素容器伸到你傳遞作爲一個依賴於這個類,然後在你的單元測試通過一個模擬的容器,而不是
  • 一個單獨的類使用反射訪問私人會員
0

另一個黑客是使用PrivateObject。個人而言,我會使用DI(依賴注入),或者爲了單元測試而使用private,internal

1

您不應該測試隊列的行爲 - 這是您可以在不更改方法行爲的情況下更改的實現細節。例如,您可以將ConcurrentQueue替換爲其他數據結構(可能是樹),而無需更新單元測試。

您正在測試的是此方法接受輸入並按您的預期存儲值。因此如

public int GetInt(int id) 

然後你測試的方法插入你希望使用相同的ID檢索它們,你需要詢問系統狀態的某種方式。

您應該測試公共方法返回所有您可以想到的結果,並將其留在方法中,以便按照它認爲合適的方式存儲值。因此,我可能會測試這樣的方法,以不同的輸入:

 [TestCase(1,2,3)] // whatever test cases make sense for you 
     [TestCase(4,5,6)] 
     [TestCase(7,8,9)] 
     [Test] 
     public void Test_InsertInt_OK(int testId, int testValue, int expectedValue) 
     { 
      InsertInt(testId, testValue); 
      Assert.AreEqual(GetInt(testId), expectedValue) 
     } 
相關問題