What are the advantages to wrapping system objects (File, ServiceController, etc) using the Adapter pattern vs. detouring for unit testing?
Consider the following method that stops a service:
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
In order to unit test the the method I basically have two options:
- Use the Adapter pattern to wrap the
ServiceController
class's methods I need into an interface I can control. This interface can then be injected into the service class (a.k.a Inversion of Control). This way I have a loosely coupled code and can use 开发者_开发知识库the traditional mocking frameworks to test. - Keep the class as is and use
Microsoft Moles (or any other code
detouring framework) to intercept
the calls to
ServiceController
to return canned results for testing purposes.
I agree that for domain model code that using the "traditional" unit testing approach makes the most sense as this would lead to a design that is easiest to maintain. However, for code that deals with the .net implementation of Windows API related stuff (file system, services, etc), is there really an advantage to going thru the extra work to get "traditionally" testable code?
It's hard for me to see the disadvantages of using Microsoft Moles for things such as ServiceController
(or the File
object). I really don't see any advantage of doing the traditional approach in this case. Am I missing anything?
Great question btw.. Just had a look at MS Moles video right now. Although I'm skeptical of MS Unit-testing tools, I must say this one looks interesting. My comparison stands at:
Adapter/Facade
- Pro: allows you to extract a meaningful role with intention revealing methods. e.g.
ServiceManager.StartService(name)
could abstract the details {1. ServiceController.GetServices(), 2. handle case where ServiceController.Status != Stopped, 3. ServiceController.Start()}. The mock/fake approach here would involve less work than setting up 3 delegates. Here this approach is an opportunity to improve your design by coming up with meaningful contracts/interfaces (also allows you to hide stuff that you don't care about -- e.g. Winapi semantics, constants, etc) - Pro: Mocking frameworks would give you better diagnostics for argument checks, number of times called, expectations not called etc.
Interceptor
- Pro: Less work if you're just interested in stubbing out a problematic call on a dependency
- Pro: definitely a good tool in your toolbox when dealing with legacy code (where the fear of change is overwhelming)
- Con: does it have a MSTest dependency? Initial searches seem to indicate that you need some plugins or extensions if you're not using MSTest.
I believe that this is a good case for mocking and there're some advantages of doing it with IoC:
You do actual unit-testing, as your tests aren't testing underlying layers - this would be an integration test -.
Easy to plug and unplug a mock object.
You don't need to define a "fake" implementation on each stub. You've a "fake", mocked implementation by configuration.
For me, the first reason is the most important.
Why don't to use Moles? Because I believe it's not the right tool for something like this. Moles is more for things like I want a fixed value of DateTime.Now to an specific time, so you can test some code in some situation whenever you want with no troubles.
Moles is an easy way to auto-mock certain methods, properties in specific tests, while IoC+fake, it's to isolate your test.
精彩评论