2009-11-19 64 views
27

測試方法調用順序目前我有:如何使用起訂量

[Test] 
    public void DrawDrawsAllScreensInTheReverseOrderOfTheStack() { 
     // Arrange. 
     var screenMockOne = new Mock<IScreen>(); 
     var screenMockTwo = new Mock<IScreen>(); 
     var screens = new List<IScreen>(); 
     screens.Add(screenMockOne.Object); 
     screens.Add(screenMockTwo.Object); 
     var stackOfScreensMock = new Mock<IScreenStack>(); 
     stackOfScreensMock.Setup(s => s.ToArray()).Returns(screens.ToArray()); 
     var screenManager = new ScreenManager(stackOfScreensMock.Object); 
     // Act. 
     screenManager.Draw(new Mock<GameTime>().Object); 
     // Assert. 
     screenMockOne.Verify(smo => smo.Draw(It.IsAny<GameTime>()), Times.Once(), 
      "Draw was not called on screen mock one"); 
     screenMockTwo.Verify(smo => smo.Draw(It.IsAny<GameTime>()), Times.Once(), 
      "Draw was not called on screen mock two"); 
    } 

但在我畫的生產代碼我對象的順序並不重要。我可以先做一個,或者兩個都沒關係。不過,它應該很重要,因爲抽籤順序很重要。

你如何(使用Moq)確保按特定順序調用方法?

編輯

我擺脫了該測試。繪製方法已從我的單元測試中刪除。我只需要手動測試它的工作。順序的逆轉被分成了一個單獨的測試類,它被測試,所以它並不全是壞的。

感謝您提供關於他們正在查看的功能的鏈接。我當然希望它很快就會被添加,非常方便。

+0

看看這個答案還有:http://stackoverflow.com/a/39692781/205859 – 2016-09-25 23:28:36

回答

4

它似乎目前尚未實施。見Issue 24: MockSequenceThis thread討論了這個問題。

不過你可能會考慮修改你的測試。我通常認爲測試順序會導致脆弱的測試,因爲它通常測試實現細節。

編輯:我不確定這是否解決OP的問題。 Lucero的回答可能會更有幫助。

+1

斷言/驗證順序可能會導致脆弱的測試,但不是因爲它正在測試實施細節。如果您正在使用Mocks,特別是使用控制反轉(依賴注入)模式的類,則您已經在測試實現細節;這就是我想說的。我會說測試訂單不應該是您測試中的常見模式,但有些情況下,沒有替代方案可以用TDD驅動正確的代碼。 – 2011-11-02 21:13:46

+1

@JesseWebb - 我認爲我們只是不同意語義。讓我們同意一些測試對內部實現細節有不適當的瞭解。只要公共API保持不變,我更喜歡在被測系統重構時不會中斷的測試。正如你所說,有些測試訂單的情況,但並不常見。 – TrueWill 2011-11-02 21:49:51

+0

您所描述的測試(僅關注被測系統的API)和使用mock的測試之間的區別在於,前者只能是真正的單元測試,如果該類沒有依賴關係。如果這個類有任何依賴關係,並且你沒有使用mock,那麼這些都是功能測試。我同意,黑盒和白盒測試都有價值。 – 2011-11-03 14:29:42

3

看看這個blog post,它可以解決你的問題。

+0

無法獲取這在我的情況下工作,我無法使用隊列交換數組。儘管現在不重要。請參閱編輯。 – Finglas 2009-11-19 19:52:04

+0

不錯的鏈接! – TrueWill 2009-11-19 20:20:13

1

否則,您可以使用回調函數並遞增/存儲callIndex值。

30

我最近創建了Moq.Sequences,它提供了檢查Moq中排序的功能。您可能需要閱讀我post描述如下:

  • 支持方法調用,財產 getter和setter方法。
  • 允許您指定 特定呼叫的次數應爲 。
  • 提供循環,允許您將 組呼叫組成循環組。
  • 允許您指定預計循環次數 。
  • 預計按順序呼叫 的呼叫可以與以任何順序預計的 呼叫相互混合。
  • 多線程支持。

典型用法是這樣的:

[Test] 
public void Should_show_each_post_with_most_recent_first_using_sequences() 
{ 
    var olderPost = new Post { DateTime = new DateTime(2010, 1, 1) }; 
    var newerPost = new Post { DateTime = new DateTime(2010, 1, 2) }; 
    var posts = new List<Post> { newerPost, olderPost }; 

    var mockView = new Mock<BlogView>(); 

    using (Sequence.Create()) 
    { 
     mockView.Setup(v => v.ShowPost(newerPost)).InSequence(); 
     mockView.Setup(v => v.ShowPost(olderPost)).InSequence(); 

     new BlogPresenter(mockView.Object).Show(posts); 
    } 
} 
+2

這是Moq的一個很棒的擴展;對於我來說缺乏序列支持是項目中的主要疏忽。國際海事組織這應該被拉入幹線(https://github.com/dwhelan/Moq-Sequences)。 – briantyler 2011-12-22 12:45:27

+1

如何在一個序列中對不同的moq對象執行調用 - 是否支持? – chester89 2012-05-04 06:55:03

0

從原來的職位,我可以假設的測試方法進行以下操作撥打:

var screenOne = new Screen(...); 
var screenTwo = new Screen(...); 
var screens = new []{screenOne, screenTwo}; 
var screenManager = new ScreenManager(screens); 
screenManager.Draw(); 

在哪裏「畫」的方法實現的東西像這樣:

public class ScreenManager 
{ 
    public void Draw() 
    { 
     _screens[0].Draw(); 
     _screens[1].Draw(); 
    } 
} 

從m y的觀點,如果呼叫順序非常重要,那麼應該在系統中引入額外的結構(描述順序)。

最簡單的實現:每個屏幕都應該知道他的後繼元素和身子後調用其繪製方法:

// 1st version 
public class Screen(Screen screenSubSequent) 
{ 
    private Screen _screenNext; 
    public Screen(Screen screenNext) 
    { 
     _screenNext=screenNext; 
    } 
    public void Draw() 
    { 
     // draw himself 
     if (_screenNext!=null) _screenNext.Draw(); 
    } 
} 

public class ScreenManager 
{ 
    public void Draw() 
    { 
     _screens[0].Draw(); 
    } 
} 

static void Main() 
{ 
    var screenOne = new Screen(null, ...); 
    var screenTwo = new Screen(screenOne, ...); 
    var screens = new []{screenOne, screenTwo}; 
    var screenManager = new ScreenManager(screens); 
} 

從一個點,每個屏幕元素應該知道一些關於另一個。這並不總是好的。如果是這樣的話:你可以創建一些類如'ScreenDrawer'。這個對象將存儲的屏幕和後續屏幕(可能是從Screen類繼承他使用其他的世界:「ScreenDrawer」類描述系統的結構下面是一個實現簡單的場景:

// 2nd version 
public class ScreenDrawer 
{ 
    private Screen _screenNext; 
    public ScreenDrawer(Screen screenNext, ...) : base (...) 
    { 
     _screenNext=screenNext; 
    } 
    public void Draw() 
    { 
     // draw himself 
     if (_screenNext!=null) _screenNext.Draw(); 
    } 
} 

public class ScreenManager 
{ 
    public void Draw() 
    { 
     _screens[0].Draw(); 
    } 
} 

static void Main() 
{ 
    var screenOne = new ScreenDrawer(null, ...); 
    var screenTwo = new ScreenDrawer(screenOne, ...); 
    var screens = new []{screenOne, screenTwo}; 
    var screenManager = new ScreenManager(screens); 
} 

第二個方法引入額外的繼承,但不要求屏幕類知道他的子序列元素

摘要:這兩種方法都進行次序調用,不需要「序列」測試,而是需要測試當前屏幕是否調用另一個,測試'ScreenManager'是否依次調用第一個元素的'Draw'方法。

這種方法:

  1. 更多的可測試性(可以使用大多數測試框架實現,無需支持'序列測試');
  2. 更穩定(沒有人可以輕易改變序列:嗨不僅需要更新源代碼,還需要更新少量測試);
  3. 更多面向對象(您正在使用對象,而不是像'序列'這樣的抽象實體);
  4. 因此:更支持。

謝謝。

10

使用起訂量回調,一個簡單的辦法:

[TestMethod] 
    public void CallInOrder() 
    { 
     // Arrange 
     string callOrder = ""; 

     var service = new Mock<MyService>(); 
     service.Setup(p=>p.FirstCall()).Returns(0).CallBack(()=>callOrder += "1"); 
     service.Setup(p=>p.SecondCall()).Returns(0).CallBack(()=>callOrder += "2"); 

     var sut = new Client(service); 

     // Act 
     sut.DoStuff(); 

     // Assert 
     Assert.AreEqual("12", callOrder); 
    } 
+0

不起作用,它表示它不能從使用中推斷出類型 – DaveH 2014-03-10 21:35:22

+0

@DaveH你是否嘗試過調試?哪一行有問題? – 2014-03-17 20:02:28

+0

@DaveH而不是「p => callOrder」使用「()=> callOrder」 – 2014-06-13 15:40:19