我有一个打开两个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()。 =]
答案 0 :(得分:1)
ServiceHostBase.Open
调用了CommunicationObject.Open
。作为其实现的一部分,它做了很多不同的事情 - 比如验证对象的状态,创建其他对象,调用方法,属性等等。由于它不是虚拟的(你的假设是正确的),Rhino将调用基类实现。
为了使它工作,你可能不得不模拟和存储许多CommunicationObject
依赖项,它仍然无法确定你是否会成功(某些类型/方法可能只是 Rhino不可撼动的,认为静态,密封或其他非虚拟的)。这就是你应该:
.Open
进行测试ServiceHostBase
周围引入wrapper并通过接口将其作为依赖项传递;这涉及额外的工作(新界面和简单的包装类),但允许你完全按照自己想要的方式进行请记住,添加包装器只会进一步委托问题(对于包装器类'Open
方法,本质上会调用ServiceHostBase.Open
- 如果您也进行单元测试吗?)。另一方面,集成测试可能无法像单元测试那样快速地捕获问题(假设您不经常运行它们)。根据您考虑OnOpen
的重要程度,您选择的方式或多或少是判断。