如何确认我的单元测试中的ServiceHost上调用了Open()

时间:2012-09-03 13:58:11

标签: c# unit-testing mocking rhino-mocks servicehost

我有一个打开两个WCF服务的Windows服务。我想单元测试OnStart()并断言正在调用service1.Open()和service2.Open()。 OnStart()看起来像这样:

protected override void OnStart(string[] args)
{
    // host WCF services            
    _service1.Open();           
    _service2.Open();
}

我正在构造函数重载中注入服务:

public WinService(ServiceHostBase service1, 
                                  ServiceHostBase service2)
{
    _service1 = service1;
    _service2 = service2;
    InitializeComponent();
}

我正在使用RhinoMocks生成ServiceHostBase的Stub,如下所示:

[TestMethod()]
public void WinServiceOnStartCallsDependenciesAsExpected()
{
    ServiceHostBase service1 = MockRepository.GenerateStub<ServiceHostBase>();
    ServiceHostBase service2 = MockRepository.GenerateStub<ServiceHostBase>();
    WinService target = new WinService(service1, service2);
    WinService_Accessor privateTarget = new WinService_Accessor(new PrivateObject(target));     
    privateTarget.OnStart(null);

当我的测试调用OnStart()时,我在调用service1.Open()时得到一个空引用异常。我已经确认service1在这一点上是一个模拟对象,并且它是抛出null的Open()。我知道Open()实际上是System.ServiceModel.Channels.CommunicationObject上的一个方法,我也尝试了Stubbing或Mocking,但我仍然得到了对象引用错误。它不是一个虚方法,所以我认为它不会被模拟版本覆盖,但是当我尝试设置Expectation reportservice.Stub(r => r.Open())时,我得到一个不同的例外,即没有默认超时,就好像它正在运行实际的CommunicationObject Open()方法,而不是抛出空引用的RhinoMocky。

所有这些都说,我只是在寻求帮助来确认在单元测试中我的ServiceHost上是否正在调用Open()。 =]

1 个答案:

答案 0 :(得分:1)

ServiceHostBase.Open调用了CommunicationObject.Open。作为其实现的一部分,它做了很多不同的事情 - 比如验证对象的状态,创建其他对象,调用方法,属性等等。由于它不是虚拟的(你的假设是正确的),Rhino将调用基类实现。

为了使它工作,你可能不得不模拟和存储许多CommunicationObject依赖项,它仍然无法确定你是否会成功(某些类型/方法可能只是 Rhino不可撼动的,认为静态,密封或其他非虚拟的)。这就是你应该:

的原因
  1. 将这种测试留给集成测试;然后,您将在最终用户环境中对.Open进行测试
  2. ServiceHostBase周围引入wrapper并通过接口将其作为依赖项传递;这涉及额外的工作(新界面和简单的包装类),但允许你完全按照自己想要的方式进行
  3. 请记住,添加包装器只会进一步委托问题(对于包装器类'Open方法,本质上会调用ServiceHostBase.Open - 如果您也进行单元测试吗?)。另一方面,集成测试可能无法像单元测试那样快速地捕获问题(假设您不经常运行它们)。根据您考虑OnOpen的重要程度,您选择的方式或多或少是判断。