2015-04-17 73 views
7

我想moq一個有索引的屬性,我希望能夠在回調中使用索引值,就像你可以使用方法一樣moq'd方法的回調參數。可能比較容易證明一個例子:Moq索引屬性,並使用返回/回調中的索引值

public interface IToMoq 
{ 
    int Add(int x, int y); 
    int this[int x] { get; set; } 
} 

    Action<int, int> DoSet = (int x, int y) => 
    { 
     Console.WriteLine("setting this[{0}] = {1}", x, y); 
     throw new Exception("Do I ever get called?"); 
    }; 
    var mock = new Mock<IToMoq>(MockBehavior.Strict); 

    //This works perfectly 
    mock.Setup(m => m.Add(It.IsAny<int>(), It.IsAny<int>())) 
     .Returns<int, int>((a, b) => a + b); 

    //This compiles, but my callback never seems to be called 
    mock.SetupSet(m => m[It.IsAny<int>()] = It.IsAny<int>()) 
     .Callback(DoSet); 

    var obj = mock.Object; 
    Console.WriteLine("Add(3,4) => {0}", obj.Add(3, 4)); //Works perfectly 
    obj[1] = 2; //Does not throw, why? 

編輯:爲了澄清,我想爲get回調/收益法是Func<int,int>,併爲set回調/收益法是Action<int,int>。試圖邁克建議,您可以排序的做到這一點的set,但有一個主要的限制:

mock.SetupSet(m => m[23] = It.IsAny<int>()) 
      .Callback(DoSet).Verifiable(); 

回調DoSet的確然後用值(23,<any>)調用。不幸的是,使用It.IsAny<int>()而不是23似乎表現爲0,而不是<any>

此外,我找不到SetupGetReturns的方式,其中Returns需要Func<int,int>甚至編譯。

可以使用Moq嗎?

動機:我只是在玩Moq,試圖用它來提供一個流暢的API來執行攔截。也就是說,如果接口I和的實例I自動創建一個Mock<I>代理,其行爲默認爲X

直接使用Castle DP可能會更有意義,但我喜歡Moq Expression Tree語法。

+0

看起來像一個bug給我。使用mock.SetupSet設置模擬(c => c [1] = It.IsAny ())。Callback(DoSet);'按預期工作,'mock.SetupSet(c => c [It.IsAny ()] = It.IsAny ())。回調(DoSet);'MockBehavior.Strict'失敗,並且不執行'MockBehavior.Loose'。 – sloth

+0

With'mock.SetupSet(x => x [It.IsAny ()] = It.IsAny ())。回調(doSet);''和'obj [0] = 1; obj [1] = 2;'它按照零指數的預期工作。它在索引1(或其他任何事)上失敗。 –

+0

@Rob:你跑哪個版本的Moq?我其實碰到了兩種不同的行爲。我正在檢查Moq的版本,我在兩個單獨的項目中運行,一個似乎工作,另一個不工作。 –

回答

1

方法SetupSet需要一個普通的委託,而不是像許多其他Moq方法那樣的Expression<...>類型的表達式樹。

因此Moq看不到您使用的是It.IsAny。相反,It.IsAny(不是我們想要的)和Moq看到它的返回值只有這恰好是default(int)0

1

從.NET Framework 4.5的Moq 4.2.1502.0911開始,我發現以下行爲是正確的。

您遇到的問題具體是由於撥打It.IsAny<T>所致。如果使用It.IsAny<T>,回調不執行:

[Test] 
public void DoesNotExecuteCallback() 
{ 
    // Arrange 
    var mock = new Mock<IIndexable>(); 

    var called = false; 
    mock.SetupSet(m => m[It.IsAny<int>()] = It.IsAny<int>()).Callback<int,int>((x, y) => called = true); 

    var instance = mock.Object; 

    // Act 
    instance[1] = 2; 

    // Arrange 
    Assert.That(called, Is.False); 
} 

但是,如果你使用特定的參數調用它,它會調用回調:

[Test] 
public void DoesExecuteCallback() 
{ 
    // Arrange 
    var mock = new Mock<IIndexable>(); 

    var called = false; 
    mock.SetupSet(m => m[1] = 2).Callback<int,int>((x, y) => called = true); 

    var instance = mock.Object; 

    // Act 
    instance[1] = 2; 

    // Arrange 
    Assert.That(called, Is.True); 
} 

總之,你應該避免使用It.IsAny的當試圖設置索引器的期望時。在一般情況下,你應該避免使用它,因爲它可以鼓勵馬虎測試寫入

適當的用例It.IsAny,但不知道具體細節我傾向於建議反對。

+0

我應該說,我正在使用Moq for可以不被視爲單元測試。 – Rob

+1

你可以對你想要做什麼做更具體的描述嗎?雖然這一個非常具體的情況似乎不被支持,但如果你發佈了你真正想做的事情,我們可能會找到一個替代方案。 –