2011-09-02 79 views
150

我有這樣一個測試:不同的返回值第一和第二時間起訂量

[TestCase("~/page/myaction")] 
    public void Page_With_Custom_Action(string path) { 
     // Arrange 
     var pathData = new Mock<IPathData>(); 
     var pageModel = new Mock<IPageModel>(); 
     var repository = new Mock<IPageRepository>(); 
     var mapper = new Mock<IControllerMapper>(); 
     var container = new Mock<IContainer>(); 

     container.Setup(x => x.GetInstance<IPageRepository>()).Returns(repository.Object); 

     repository.Setup(x => x.GetPageByUrl<IPageModel>(path)).Returns(() => pageModel.Object); 

     pathData.Setup(x => x.Action).Returns("myaction"); 
     pathData.Setup(x => x.Controller).Returns("page"); 

     var resolver = new DashboardPathResolver(pathData.Object, repository.Object, mapper.Object, container.Object); 

     // Act 
     var data = resolver.ResolvePath(path); 

     // Assert 
     Assert.NotNull(data); 
     Assert.AreEqual("myaction", data.Action); 
     Assert.AreEqual("page", data.Controller); 
    } 

GetPageByUrl我dashboardpathresolver運行兩次,我怎麼能告訴最小起訂量返回NULL首次pageModel.Ojbect的第二?

回答

27

添加的回調並沒有爲我工作,我用這個方法來代替http://haacked.com/archive/2009/09/29/moq-sequences.aspx和我結束了這樣一個測試:

[TestCase("~/page/myaction")] 
    [TestCase("~/page/myaction/")] 
    public void Page_With_Custom_Action(string virtualUrl) { 

     // Arrange 
     var pathData = new Mock<IPathData>(); 
     var pageModel = new Mock<IPageModel>(); 
     var repository = new Mock<IPageRepository>(); 
     var mapper = new Mock<IControllerMapper>(); 
     var container = new Mock<IContainer>(); 

     container.Setup(x => x.GetInstance<IPageRepository>()).Returns(repository.Object); 
     repository.Setup(x => x.GetPageByUrl<IPageModel>(virtualUrl)).ReturnsInOrder(null, pageModel.Object); 

     pathData.Setup(x => x.Action).Returns("myaction"); 
     pathData.Setup(x => x.Controller).Returns("page"); 

     var resolver = new DashboardPathResolver(pathData.Object, repository.Object, mapper.Object, container.Object); 

     // Act 
     var data = resolver.ResolvePath(virtualUrl); 

     // Assert 
     Assert.NotNull(data); 
     Assert.AreEqual("myaction", data.Action); 
     Assert.AreEqual("page", data.Controller); 
    } 
20

設置模擬對象時可以使用回調。看看Moq Wiki的例子(http://code.google.com/p/moq/wiki/QuickStart)。

// returning different values on each invocation 
var mock = new Mock<IFoo>(); 
var calls = 0; 
mock.Setup(foo => foo.GetCountThing()) 
    .Returns(() => calls) 
    .Callback(() => calls++); 
// returns 0 on first invocation, 1 on the next, and so on 
Console.WriteLine(mock.Object.GetCountThing()); 

您的設置看起來像這樣:

var pageObject = pageModel.Object; 
repository.Setup(x => x.GetPageByUrl<IPageModel>(path)).Returns(() => pageObject).Callback(() => 
      { 
       // assign new value for second call 
       pageObject = new PageModel(); 
      }); 
+0

我兩次都得到null當我這樣做: var pageModel = new Mock (); IPageModel model = null; (())=> { model = pageModel.Object; }); – Marcus

+0

GetPageByUrl在resolver.ResolvePath方法中調用兩次嗎? – Dan

+0

是它被調用兩次 – Marcus

92

現有的答案是偉大的,但我認爲我會拋出我的選擇,它只是使用System.Collections.Generic.Queue,並不需要任何特殊的知識的嘲笑框架 - 因爲我沒有任何時候我寫的! :)

var pageModel = new Mock<IPageModel>(); 
IPageModel pageModelNull = null; 
var pageModels = new Queue<IPageModel>(); 
pageModels.Enqueue(pageModelNull); 
pageModels.Enqueue(pageModel.Object); 

則...

repository.Setup(x => x.GetPageByUrl<IPageModel>(path)).Returns(pageModels.Dequeue); 
+0

謝謝。我只是修復了排版pageModel.Object的pageModel模擬的錯字,現在它甚至應該也會生成! :) –

+3

答案是正確的,但是請注意,如果你想拋出一個'Exception',因爲你不能'Enqueue'它不會工作。但'SetupSequence'將起作用(例如,請參閱@stackunderflow的答案)。 – Halvard

+2

您必須爲Dequeue使用委託方法。樣本寫入的方式總是會重複返回隊列中的第一個項目,因爲在設置時評估出隊隊列。 –

262

隨着起訂量的最新版本(4.2.1312.1622),您可以設置使用SetupSequence的一系列事件。這裏有一個例子:

_mockClient.SetupSequence(m => m.Connect(It.IsAny<String>(), It.IsAny<int>(), It.IsAny<int>())) 
     .Throws(new SocketException()) 
     .Throws(new SocketException()) 
     .Returns(true) 
     .Throws(new SocketException()) 
     .Returns(true); 

調用connect才能取得成功的第三和第五次嘗試否則會拋出異常。

因此,對於你的例子這純粹是這樣的:

repository.SetupSequence(x => x.GetPageByUrl<IPageModel>(virtualUrl)) 
.Returns(null) 
.Returns(pageModel.Object); 
+1

好的答案,唯一的限制是「SetupSequence」不能與受保護的成員一起使用。 – Chasefornone

+1

唉,'SetupSequence()'不能和'Callback()'一起使用。如果只有它,那麼可以用「狀態機」方式來驗證對模擬方法的調用。 – urig

1

這裏達到了同樣的問題略有不同的要求。
我需要得到來自總部設在不同的輸入模擬值不同的返回值和IMO更具可讀性,因爲它使用起訂量的聲明語法(LINQ到嘲笑)找到解決方案。

public interface IDataAccess 
{ 
    DbValue GetFromDb(int accountId); 
} 

var dataAccessMock = Mock.Of<IDataAccess> 
(da => da.GetFromDb(It.Is<int>(acctId => acctId == 0)) == new Account { AccountStatus = AccountStatus.None } 
&& da.GetFromDb(It.Is<int>(acctId => acctId == 1)) == new DbValue { AccountStatus = AccountStatus.InActive } 
&& da.GetFromDb(It.Is<int>(acctId => acctId == 2)) == new DbValue { AccountStatus = AccountStatus.Deleted }); 

var result1 = dataAccessMock.GetFromDb(0); // returns DbValue of "None" AccountStatus 
var result2 = dataAccessMock.GetFromDb(1); // returns DbValue of "InActive" AccountStatus 
var result3 = dataAccessMock.GetFromDb(2); // returns DbValue of "Deleted" AccountStatus 
0

accepted answer,還有SetupSequence answer,處理返回常量。

Returns()有一些有用的重載,你可以返回基於該被送往嘲笑方法的參數值。根據接受的答案中給出的the solution,這裏是對這些重載的另一種擴展方法。

public static class MoqExtensions 
{ 
    public static IReturnsResult<TMock> ReturnsInOrder<TMock, TResult, T1>(this ISetup<TMock, TResult> setup, params Func<T1, TResult>[] valueFunctions) 
     where TMock : class 
    { 
     var queue = new Queue<Func<T1, TResult>>(valueFunctions); 
     return setup.Returns<T1>(arg => queue.Dequeue()(arg)); 
    } 
} 

不幸的是,使用該方法需要你指定一些模板參數,但結果仍然是相當可讀。

repository 
    .Setup(x => x.GetPageByUrl<IPageModel>(path)) 
    .ReturnsInOrder(new Func<string, IPageModel>[] 
     { 
      p => null, // Here, the return value can depend on the path parameter 
      p => pageModel.Object, 
     }); 

創建用於多個參數,如果需要(T2T3等)擴展方法過載。

相關問題