2010-01-28 197 views
6

我正在尋找使下面更簡潔的方法。乾燥與犀牛嘲笑

public class MyTests 
{ 
    IPresenter presenter; 

    [SetUp] 
    public void SetUp() 
    { 
     presenter = MockRepository.GenerateStub<IPresenter>(); 
    } 

    ... 
} 

特別是在創建模擬時重新指定類型似乎是多餘的。例如,我可以寫這樣的,使用反射來獲取類型並自動創建存根:

public class MyTests 
{ 
    IPresenter presenter; 

    [SetUp] 
    public void SetUp() 
    { 
     Stub(x => x.presenter); 
    } 

    void Stub(Expression<Func<MyTests, object>> expression) 
    { 
     ... 
    } 
} 

這會工作,但編譯器可以不再檢測演示分配,並開始發出警告。這也使得ReSharper非常不高興。

任何人都可以提出更好的方法嗎?

回答

5

這可能是有爭議的,但我贊成可讀性,而非單元測試中的DRY-ness *。

換句話說,在我的單元測試中,設置方法是不存在的。它們僅用於集成測試。我相信XUnit.NET也是這樣。

所以要回答你的問題,我真的不會擔心在每個需要測試的測試中設置模擬演示者。一些測試可能不需要模擬演示者,因此在測試運行之前設置一個測試是沒有必要的。當然,我的單元測試跨度說平均十行,如果這增加或設置測試的範圍(遵循AAA-Arrange,Act Assert)很大,那麼我將刪除重複並創建幫助程序方法。要清除這一點,對於更清潔的測試,您可以創建一個包含幫助程序方法和其他設置代碼的基本測試類。*

+1

我大多同意這個(因此+1),但也想建議你可以爲模擬測試創建一個內部類,如果你將有很多使用它們的測試用例。 – jonnii 2010-01-28 22:45:00

+0

@jonni - 你擊敗了我的編輯。但我同意。 – Finglas 2010-01-28 22:46:54

+0

@finglas這是一個協議的圈子。讓我們相互在後面拍; – jonnii 2010-01-28 22:48:49

3

是的,完全不使用[Setup]和成員變量,而是使用創建方法編寫Fixture Objects

夾具對象只會保持夾具的適當模擬和其他部分。

我個人使用AutoFixture as a Fixture Object,並將它設置爲自動嘲諷集裝箱啓動,所以我沒有寫任何代碼模擬,除非我需要明確定義一些行爲。

下面是最近的樣本單元測試:

[TestMethod] 
public void DeleteProductWillDeleteProductFromRepository() 
{ 
    // Fixture setup 
    var fixture = new ServiceFixture(); 
    var id = fixture.CreateAnonymous<int>(); 
    var repMock = fixture.FreezeMoq<ProductRepository>(); 

    var sut = fixture.CreateAnonymous<ProductManagementService>(); 
    // Exercise system 
    sut.DeleteProduct(id); 
    // Verify outcome 
    repMock.Verify(r => r.DeleteProduct(id)); 
    // Teardown 
} 

在這種情況下,repMock由起訂量創建的,但我可以將其設置爲使用犀牛製品代替。

+1

沒有[設置]或[拆解]的xUnit FTW! – mxmissile 2010-01-28 22:45:38

+0

您正在交易成員變量+初始值設定項,以便在每種方法中進行額外設置,這些方法將與多個測試相結合。 自動嘲笑是我應該看看,但謝謝。 – 2010-01-29 08:58:09

0

它與C#的一般疼痛,它不會從方法的結果(與Java不同),並且在很多情況下都很痛苦(僅舉一個例子,你想實現去串行化方法)。就我個人而言,我不喜歡使用var關鍵字,因爲我想看看到底是什麼類型的對象,我寧願跳過模板中的類型名稱。

無論如何,如果我的測試非常好,我傾向於初始化每個測試用例中的所有模擬。通過這種方式,您可以查看與其他測試分離的每個測試,並立即看到發生的情況。如果他們變長了,儘管我只是用你在問題開始時粘貼的討厭的方式。

1

Michael Feathers對此有着出色的看法(參見他的介紹http://www.ndc2010.no/index.aspx?id=361621)。創建建設者,並在測試中使用它,而不是所有類型的設置。

像:

//The Class to Test 
    public class ObjectY 
    { 
     public string DoThis(IObjectX objectX) 
     { 
      return objectX.id + objectX.name; 
     } 
    } 


    [Test] 
    //The test 
    public void CreaeteTestData() 
    { 
     //Almost prosa style creation of test data 
     var testData = new ObjectXBuilder().WithId(123).WithName("ABC").Build(); 

     Assert.That(new ObjectY().DoThis(testData), Is.EqualTo("123ABC")); 

    } 


    //The Builder class - Provides easy creation testdata. 
    internal class ObjectXBuilder 
    { 
     private MockRepository _mockRepository; 
     private IObjectX _objectX; 

     public ObjectXBuilder() 
     { 
      _mockRepository = new MockRepository(); 
      _objectX = _mockRepository.Stub<IObjectX>(); 
     } 

     public ObjectXBuilder WithName(string name) 
     { 
      _objectX.name = name; 
      return this; 
     } 

     public ObjectXBuilder WithId(long id) 
     { 
      _objectX.id = id; 
      return this; 
     } 

     public IObjectX Build() 
     { 
      _mockRepository.ReplayAll(); 
      return _objectX; 
     } 

    }