2013-10-17 19 views
0

我目前正在編寫一個類的單元測試,該類根據大XML文件中找到的參數來設置值的格式。可以模擬我正在測試的課程的某些部分嗎?

我正在測試的類在其構造函數中接收另一個類,該類提供瞭解析和讀取xml文件的功能。我認爲給測試類提供一個xml閱讀類的具體實例是不好的風格,因爲我相信這樣做會導致在每次我想要測試xml閱讀類時 - 實際上 - 測試主類的格式化函數。如果在xml閱讀類中出現問題,格式化類中的所有單元測試都會失敗,這顯然不是格式類的錯誤。

那麼我該如何繼續?

很明顯,我只是創建一個XML讀取類的模擬並將其作爲參數傳遞給構造函數。但是,格式化類將使用此實例來創建約5個其他類的私有實例。因爲我不知道這些類想做什麼(老實說這些測試不應該在意),我想嘲笑我正在測試的這個類的這些私有字段。

這甚至可以嗎?我將如何使用Moq做到這一點?

CNC中

請看下面的例子:

public class FormatterCore : IFormatterInterfaceIWantToTest 
{ 
    public FormatterCore(IConfigService service) 
    { 
     this.something = new SomeStuffA(service); 
     this.somethingThatINeed = new SomethingUserfull(service); 
     this.somethingElse = new SomeOtherStuff(service); 
     this.somethingTotallyDifferent = new SomeReallyUselessStuff(service); 
     //... 
    }  
    public T Format<T>(object input, string id) 
    { 
     // implementation of the interface I want to test 
    } 
} 

在我的例子我想測試接口的方法Format<T>()。要創建Formatter類的實例,我需要傳遞一個IConfigService實現的實例(這是昂貴且麻煩的,因爲它需要不同的xml文件並需要一段時間)。我的問題在於,我不想爲每個單元測試創​​建一個configService的實例,因爲這意味着我會在FormatterCore單元中的每個測試中測試configService本身。

+1

*然而,格式化類將使用此實例來創建約5個其他類的私有實例。*很高興看到這樣的例子。 – sloth

+0

@DominicKexel我看到了,我編輯了我最初的問題 – buddybubble

回答

0

由於您不能訪問XML格式化類的私有變量(除了通過反射入侵類),並且您無法確定何時創建其他類,所以我不認爲您可以用你喜歡的方式嘲笑他們。不得不入侵一個類來訪問私有變量或者測試方法,這是一種代碼異味 - 這意味着你有隱藏的可測試的功能應該被暴露。

所以,爲了公開這個功能,看起來最好的方法是注入XML格式化類用來創建這些其他類的工廠。您的XML閱讀器/解析器模擬將被傳遞給Create方法,並且您將返回適當的模擬這些類以供XML格式化類使用。

或者,您可以像在集成測試中那樣對待XML格式化類 - 接受使用您的XML讀取器/解析器模擬作爲參數來創建其他類,並設置該模擬以期望來自它們的調用爲好。

+0

你能詳細說說關於注射工廠的部分嗎?如果我正確理解你,這將需要對測試代碼進行更改以顯示內部字段的創建。怕我不能那樣做。 – buddybubble

+0

啊,如果你不能改變'FormatterCore',那麼注射工廠是不行的。我建議你設置你的'IConfigService'模擬以期待來自'SomeStuffA'和其他人的調用。將'FormatterCore'和它創建的類視爲一個單元。你可以使用反射來將這些私有字段設置爲其他模擬([如此處所述](http://stackoverflow.com/questions/10862747/access-private-fields)),但要弄清這些類之間的依賴關係並設置存根調用可能非常棘手,取決於它們如何相互作用。 –

1

爲了測試FormatterCore您不應該創建一個IConfigService實現的實例。您必須創建並設置IConfigService的模擬對象。

[TestClass] 
public class FormatterCoreTest 
{ 
    Mock<IConfigService> сonfigServiceMock; 

    [TestInitialize] 
    public void Init() 
    { 
     сonfigServiceMock = new Mock<IConfigService>(); 
    } 

    [TestMethod] 
    public void Format() 
    { 
     // arrange 
     var input = /* input value */; 
     var id = /* id value */; 
     var сonfigServiceMock 
      .Setup(services => services.YourMethodToMock()) 
      .Returnes(/* expected result or behaviour */); 

     // act 
     var target = new FormatterCore(сonfigServiceMock.Object); 

     var result = target.Format</* AnyType */>(input, id); 

     // assert 
     /* Your asserts */ 
     result.Should().Be(/* expectred result */); 
     Assert.AreEqual /* expectred result */, result); 
    } 
} 

是類型SomeStuffASomethingUserfullSomeOtherStuffSomeReallyUselessStuff嵌套,不能測試或公衆,這是可能的嗎?

如果有可能測試類型SomeStuffASomethingUserfullSomeOtherStuffSomeReallyUselessStuff那麼最好是在它們的實例注入FormatterCore的構造函數,而不是在構造函數創建它們的。

public class FormatterCore : IFormatterInterfaceIWantToTest 
{ 
    ISomeStuffA something; 
    ISomethingUserfull somethingThatINeed; 
    ISomeOtherStuff somethingElse; 
    ISomeReallyUselessStuff somethingTotallyDifferent; 

    public FormatterCore(
     ISomeStuffA someStuffA, 
     ISomethingUserfull somethingUserfull, 
     ISomeOtherStuff someOtherStuff, 
     ISomeReallyUselessStuff someReallyUselessStuff 
     ) 
    { 
     this.something = someStuffA; 
     this.somethingThatINeed = somethingUserfull; 
     this.somethingElse = someOtherStuff; 
     this.somethingTotallyDifferent = someReallyUselessStuff; 
     //... 
    } 

    public T Format<T>(object input, string id) 
    { 
     // implementation of the interface I want to test 
    } 
} 

讓你的IoC負責創建實例。

需要在每個測試中爲所有依賴項創建和設置模擬。

相關問題