使用适配器模式包装系统对象(File,ServiceController等)与unit testing绕行有什么好处?

请考虑以下停止服务的方法:

Public Function StopService(ByVal serviceName As String, ByVal timeoutMilliseconds As Double) As Boolean Try Dim service As New ServiceController(serviceName) Dim timeout As TimeSpan = TimeSpan.FromMilliseconds(timeoutMilliseconds) service.[Stop]() If timeoutMilliseconds <= 0 Then service.WaitForStatus(ServiceControllerStatus.Stopped) Else service.WaitForStatus(ServiceControllerStatus.Stopped, timeout) End If Return service.Status = ServiceControllerStatus.Stopped Catch ex As Win32Exception 'error occured when accessing a system API' Return False Catch ex As TimeoutException Return False End Try End Function 

为了对unit testing方法我基本上有两个选择:

  1. 使用Adapter模式将我需要的ServiceController类的方法包装到我可以控制的接口中。 然后可以将此接口注入服务类(也称为控制反转)。 这样我就可以使用松散耦合的代码,并可以使用传统的模拟框架进行测试。
  2. 按原样保留类,并使用Microsoft Moles(或任何其他代码绕行框架)拦截对ServiceController的调用,以返回用于测试目的的预制结果。

我同意对于使用“传统”unit testing方法的域模型代码最有意义,因为这会导致设计最容易维护。 但是,对于处理与Windows API相关的东西(文件系统,服务等)的.net实现的代码,通过额外的工作获得“传统的”可测试代码是否真的有优势?

我很难看到使用Microsoft Moles来处理诸如ServiceController (或File对象)之类的缺点。 在这种情况下,我真的没有看到采用传统方法的任何优势。 我错过了什么吗?

很棒的问题顺便说一下..现在就看看MS Moles的video吧。 虽然我对MSunit testing工具持怀疑态度,但我必须说这个看起来很有趣。 我的比较是:

适配器/门面

  • Pro:允许您通过意图揭示方法提取有意义的角色。 例如, ServiceManager.StartService(name)可以抽象细节{1。 ServiceController.GetServices(),2。处理ServiceController.Status!= Stopped,3. ServiceController.Start()}的情况。 这里的模拟/虚假方法涉及的工作少于设置3个代表。 这种方法是一个通过提出有意义的合同/接口来改进设计的机会(也允许你隐藏你不关心的东西 – 例如Winapi语义,常量等)
  • Pro:模拟框架可以为您提供更好的参数检查诊断,调用次数,未调用期望等。

拦截器

  • 亲:如果你只是想对依赖项上的有问题的调用进行存根感兴趣,那就少工作
  • 亲:在处理遗留代码时,你的工具箱中绝对是一个很好的工具(对改变的恐惧是压倒性的)
  • Con:它有MSTest依赖吗? 如果您不使用MSTest,初始搜索似乎表明您需要一些插件或扩展。

我相信这是一个很好的模拟案例,并且使用IoC实现它有一些优势:

  • 您进行实际的unit testing,因为您的测试不是测试底层 – 这将是一个集成测试 – 。

  • 易于插拔模拟对象。

  • 您不需要在每个存根上定义“假”实现。 你有一个“假的”,模拟的配置实现。

对我来说,第一个原因是最重要的。

为什么不使用鼹鼠? 因为我认为它不适合这样的事情。 Moles更像是我想要一个固定的DateTime.Now到特定的时间,所以你可以在任何情况下测试一些代码,无需麻烦。

Moles是一种在特定测试中自动模拟某些方法,属性的简单方法,而IoC +假冒,它是为了隔离您的测试。