7

處理我使用的是Microsoft單元測試,並符合下列條件:單元測試對象,它的生命週期範圍由IoC容器

public class AccountCommandHandlers : 
    Handler<CreateAccountCommand>, 
    Handler<CloseAccountCommand> 
{ 
    public bool CreateAccountCommandWasCalled = false; 
    public bool CloseAccountCommandWasCalled = false; 

    public void Handle(CreateAccountCommand command) 
    { 
     CreateAccountCommandWasCalled = true; 
    } 

    public void Handle(CloseAccountCommand command) 
    { 
     CloseAccountCommandWasCalled = true; 
    } 
} 

[TestMethod] 
public void CanRaiseInternalHandlers() 
{ 
    var iocContainer = SimpleInjectorWiringForMembus.Instance; 
    iocContainer.Bootstrap(
     AppDomain.CurrentDomain.GetAssemblies()); 

    var membus = MembusWiring.Instance; 
    membus.Bootstrap(); 

    membus.Bus.Publish(new CreateAccountCommand() { Id = 100 }); 
    membus.Bus.Publish(new CloseAccountCommand() { Id = 100 }); 
} 

我使用IoC容器(簡單噴油器),其中處理對象的生存範圍。 Membus連接命令以命令處理程序,並通過IoC容器解析。

上述代碼運行並運行,命令處理程序將其局部變量設置爲true。

但是,由於Simple Injector處理壽命範圍,因此我不能要求簡單噴射器爲AccountCommandHandler對象,因爲它會返回一個新對象,並將CreateAccountCommandWasCalled設置爲false。

單元測試新手除了將CreateAccountCommandWasCalled設置爲靜態變量之外,什麼樣的測試更加穩健?

回答

5

正如其他人已經提到,你實際上正在運行集成測試。但這不是問題。集成測試適用於測試您的IoC設置以及確保應用程序的不同部分協同工作。

但是,通過集成測試,您不應該使用模擬對象或存根對象。模擬和存根在單元測試中有其用處。單元測試就是測試代碼中儘可能最小的部分。在單元測試中,您可以使用mock來控制您的類所具有的所有依賴關係的行爲。我寫了一個blog一年前,給出了一個介紹集成和單元測試,以及如何使用您的測試嘲諷之間的差異。

在你的情況下,我不會使用帶生產配置的IoC容器來設置你的單元測試。相反,我會切換在測試中手動創建對象,並使用類似Moq嘲弄的工具來控制的依賴。

不過這也未嘗可以實現自動化。一個好工具是AutoFixture。 '夾具'是指您需要運行測試的基線。這可能是一些示例數據,您需要的模擬和存根以及其他設置代碼。

Mark Seemann(AutoFixture的開發者)幾周前寫了一篇很好的博客,關於將AutoFixture與IoC結合使用作爲Auto-mocking Container。我會建議使用這樣的東西來構建你的單元測試。

5

這裏有一個更「哲學」回答你的問題:-)

我的建議是在測試中不使用IOC容器可言,如果可能的話!

我的理由是,您需要測試來完全控制測試的上下文,並且IOC可以拿走一些控件。國際海事組織,單元測試應該儘量集中,小而且可預測!

考慮將模擬對象發送到測試中的類中,而不是實際的類。

如果你的類需要有一個IOC容器的內部實例,請將它從類中分解成某種「控制器」。

你可以用幾種方法完成這個,我最喜歡的是使用像Rhino Mocks這樣的框架。

通過這種方式,您可以在運行時,在測試「設置」中實際上將IOC提供的「生命週期」存檔。

因此,當使用像Rhino這樣的框架創建或銷燬對象時,測試應該完全控制(通過嘲弄和存根)。

如果甚至需要,您可以嘲笑IOC。

作爲一個便箋,設計良好的IOC容器的好處之一是它應該使單元測試更容易 - 因爲它會阻止類依賴實際的類的「具體實例」,並鼓勵使用可互換接口。

您應該嘗試在IOC容器上運行時提供您設計的接口的具體實現。

請注意,清楚您實際測試的內容通常也很重要。單元測試通常應該關注於測試單個類上單一方法的行爲。

如果您實際上在一個課程上測試「多於」一種方法,例如,一個類如何與其他類交互,這意味着你很可能寫了一個「集成」測試,而不是真正的「單元」測試。

另一個說明:我不認爲是單元測試的專家!它們非常有用,但我仍然努力測試幾乎任何其他方面的編碼。

爲了進一步閱讀,我強烈推薦Roy Osherove的「單元測試的藝術」。還有其他人。

The Art of Unit Testing

+0

好吧,這個答案更像是一個關於單元測試的大嚷嚷,而不是回答你的具體問題,對不起!希望有人發現它有用的一天:) – GrahamMc 2013-04-25 08:48:45

+0

我同意你在這裏說,但我覺得OP實際上正在編寫一個集成測試來驗證系統是否能正常工作。在這幾個案例中,你實際上想要測試基礎設施,包括你的IoC容器和Membus。您應該可以進行一些測試,但這些測試非常重要。 – Steven 2013-04-25 09:02:34

+0

@Steven同意! – GrahamMc 2013-04-25 13:32:49

4

正如史蒂芬在他的評論說,這聽起來像你正在寫一個集成測試,在這種情況下,它確實是有意義的使用IoC容器。

您的測試項目應該有自己的IoC配置的組合根。您可以配置您的IoC容器以返回AccountCommandHandlers的模擬對象。您的測試,而不是查布爾成員,可以改爲檢查手柄(CreateCommand)被稱爲至少一次。隨着起訂量,這將是這個樣子:

mock.Verify(foo => foo.Handle(createAccountCommand), Times.AtLeastOnce()); 

如果因任何原因,你不能使用和IoC配置一個模擬的框架,這將是很容易建立自己的模擬類爲這一個測試用例。

4

一個問題,你也應該問自己的是:什麼其實我是想測試?

您的測試套件不一定需要測試第三方庫。在上文中,membus旨在傳遞郵件的處理程序,該庫測試確保這種情況。

如果你真的想要測試的消息 - >國際奧委會處理代表團在Membus的情況下,你可以寫一個總線 - >國際奧委會適配器來進行測試:

public class TestAdapter : IocAdapter 
{ 
    private readonly object[] _handlers; 

    public TestAdapter(params object[] handlers) 
    { 
     _handlers = handlers; 
    } 

    public IEnumerable<object> GetAllInstances(Type desiredType) 
    { 
     return _handlers; 
    } 
} 

然後用下的情況下使用測試。

 var bus = 
      BusSetup.StartWith<Conservative>() 
        .Apply<IoCSupport>(
         ioc => ioc.SetAdapter(new TestAdapter(new Handler<XYZ>())) 
        .SetHandlerInterface(typeof (IHandler<>))) 
        .Construct(); 

,你會繼續處理程序實例身邊做斷言反對。OTOH如果你信任圖書館來完成它的工作,那麼你只需要新建處理程序並測試其內部邏輯。

+0

好點,但是還有一些其他的東西(序列化)用於其他測試(我沒有包括在內,以免使事情複雜化),membus實際上只被列爲不是減肥測試的人工產物。 membus是偉大的btw :) – g18c 2013-04-30 14:41:41