1

我有一个正在打开两个 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 存根,如下所示:

[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 在这一点上是一个模拟对象,并且它是 Open() 抛出空值。我知道 Open() 实际上是 System.ServiceModel.Channels.CommunicationObject 上的一个方法,我也尝试了 Stubbing 或 Mocking,但我仍然收到对象引用错误。它不是虚拟方法,所以我认为它只是没有被模拟版本覆盖,但是当我尝试设置 Expectationreportservice.Stub(r => r.Open())时,我得到一个不同的异常,即没有默认超时,就好像它正在运行实际的 CommunicationObject Open() 方法而不是抛出 null ref 的 RhinoMocky 方法。

综上所述,我只是在寻求有关如何确认在单元测试中在我的 ServiceHost 上调用 Open() 的帮助。=]

4

1 回答 1

1

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

为了使其工作,您可能必须模拟和存根大量CommunicationObject依赖项,并且仍然不确定您是否会成功(某些类型/方法可能根本无法被 Rhino模拟,认为静态、密封或其他非-虚拟的)。这就是为什么您应该:

  1. 将这种测试留给集成测试;然后,您将.Open在最终用户环境中进行测试
  2. 引入一个包装器ServiceHostBase通过接口将其作为依赖项传递;这涉及额外的工作(新接口和简单的包装类),但允许您做您想做的事

请记住,添加包装器只会将问题进一步委托(到包装器类的Open方法,它本质上会调用ServiceHostBase.Open- 你是否也应该对其进行单元测试?)。另一方面,集成测试可能无法像单元测试那样快速发现问题(假设您运行它们的频率较低)。根据您认为OnOpen的关键程度,您选择的方式或多或少是一种判断。

于 2012-09-03T14:26:41.803 回答