2011-09-02 49 views
5

這是我的第一個問題,請善待! :)如何使用Moq來測試一個沒有返回值的方法?

我想要做的是寫一些經理類的測試,在施工期間添加許多單個項目類的新實例到列表中。在此管理器類中調用UpdateAllItems時,其目的是迭代列表並在每個單獨項上調用Increment。

經理類是我的代碼,但單個項目類不是我不能修改它。

我使用NUnit作爲測試框架,並開始使用Moq。因爲經理類使用單個項目類,我認爲我需要使用Moq,所以我只測試經理,而不是單個項目。

如何爲我的UpdateAllItems方法編寫測試? (從技術上說,我應該先寫測試我知道)。

下面是一些示例代碼,讓我與我的工作的總體思路...提前

public class SingleItem_CodeCantBeModified 
{ 
    public int CurrentValue { get; private set; } 

    public SingleItem_CodeCantBeModified(int startValue) 
    { 
     CurrentValue = startValue; 
    } 

    public void Increment() 
    { 
     CurrentValue++; 
    } 
} 

public class SingleItemManager 
{ 
    List<SingleItem_CodeCantBeModified> items = new List<SingleItem_CodeCantBeModified>(); 

    public SingleItemManager() 
    { 
     items.Add(new SingleItem_CodeCantBeModified(100)); 
     items.Add(new SingleItem_CodeCantBeModified(200)); 
    } 

    public void UpdateAllItems() 
    { 
     items.ForEach(item => item.Increment()); 
    } 
} 

感謝所有幫助!

+0

這個類是如何有用的?它創建一個專用的計數器列表,並在每次調用某個方法時更新每個項目。對象的行爲必須有一些改變。如果UpdateAllItems不起作用 - 將觀察到的變化是什麼? – Gishu

回答

5

簡單的答案是,你不能。 UpdateAllItems調用的方法(Increment())是非虛擬的,因此您將無法嘲笑它。

您的選擇,在我看來,主要有:

  • 不要測試UpdateAllItems可言。它的實現是微不足道的,所以這是一個可以考慮的選項(雖然不是很理想)。
  • 在您的測試中創建實際的SingleItem_CodeCantBeModified實例。純粹主義者會說在這一點上你不再有單元測試,但它仍然是一個有用的測試。
  • 添加一個ISingleItem接口和一個SingleItemAdapter : ISingleItem類,該類保存對SingleItem_CodeCantBeModified的引用並轉發呼叫。然後,您可以編寫SingleItemManager以在ISingleItem上運行,並且您可以在測試中自由傳遞模擬ISingleItem。 (根據您的系統是如何設置的,你甚至可以從SingleItem_CodeCantBeModified下降,實現你的後裔接口,並使用這些對象,而不是寫一個適配器。)

這最後一個選項爲您提供了大多數選擇,但是以一些複雜性爲代價。選擇最適合您嘗試完成的選項。

1

你的經理太依賴於物品(在List<Item>)。你可以提取列表人口到單獨的類,以便能夠嘲笑它嗎?例如:

public SingleItemManager() 
{ 
    items.Add(ItemRepository.Get(100)); 
    items.Add(ItemRepository.Get(200)); 
} 

測試(一些代碼省略):

int i = 0; 

var itemMock = new Mock<Item>(); 
itemMock.Setup(i => i.Increment()).Callback(() => i++); 

var repositoryMock = new Moc<ItemRepository>(); 
repositoryMock.Setup(r => r.Get(It.IsAny<int>()).Returns(itemMock.Object); 

var manager = new SingleItemManager(); 
manager.UpdateAllItems(); 

Assert.AreEqual(i, 1); 
-1

而不是硬編碼的額外的具體項目,有SingleItem_CodeCantBeModified實現一個接口(或者它實現了接口的包裝,它嵌入),然後通過一個(新)工廠,這將創建這些項目。

在您的測試中,您將創建一個工廠模擬傳遞給您的管理器類,然後您可以監視該模擬對象上調用的方法。

雖然這將更多地測試系統的內部,而不是副產品。管理器實施什麼界面?如果它沒有從外部證明自己,你測試的結果是什麼?

+0

SingleItem_ ** CodeCantBeModified ** – riezebosch

+0

@riezebosch:如果push來推,您可以創建一個代理對象,實現一個interfact,並通過方法調用。它與gettting類實現接口的效果不盡相同,但效果相同。 – Chris

0

和往常一樣,您可以添加另一個間接級別。

  1. 創造約SingleItem_CodeCantBeModified
  2. 一個包裝類,使此包裝繼承IItem接口
  3. SingleItemManager取決於IItem而不是SingleItem_CodeCantBeModified

OR

如果Increment是一個虛擬的方法(我知道它不在你的示例代碼中,但是以防萬一),請使用partial mocking