8

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:

  1. 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.
  2. 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?

Matt
  • 14,353
  • 5
  • 53
  • 65
  • 4
    +1 Good question. Although I'm not sure how valuable the unit tests will be for that example, whichever route you go. The tests will just prove that you've called particular methods in a particular order - the logic in that function is very simple, you could just read the code. – MarkJ Feb 09 '11 at 17:09
  • I've thought about that as well. Does this method even need to be tested. The method (and it's class) is already using the [remote?] facade pattern as it is reducing the complexity required to stop the service. The code using the service class can be tested with the service class being mocked traditionally. So maybe we have a case of code that doesn't need to be tested, as at some point you still have to write code to access the API? How though would one write code that accomplishes the same result with TDD? Wouldn't that require testing it none-the-less, and thus the reason for this question. – Matt Feb 09 '11 at 17:34
  • 1
    @MarkJ, if you'd like to comment on the validity of testing this method (from a TDD perspective), I'd love to see your answer here: http://stackoverflow.com/questions/4953032/does-one-still-write-tests-with-tdd-when-the-desired-code-has-little-to-no-logic – Matt Feb 10 '11 at 03:29

2 Answers2

1

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.

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • You bring up some valid points, especially the DateTime comparison. Yet, "pluggability" doesn't really apply in this case as how often would the underlying code change? Unless I port over to a non windows environment, I don't see that a requirement in this case. I disagree that using Moles would make the test an integration test as one wouldn't be hitting the underlying layer at all. It would be detoured to the delegate in the unit test, very much like using a stub in Moq. – Matt Feb 09 '11 at 15:07
  • I didn't say "Moles would convert it into an integration test". This is your own conclusion of my text. I'll stick with the idea of mocking, instead of detouring, for your situation. If you want to abstract WinAPI, just do IoC and put a fake WinAPI wrapper and work with it in testing. – Matías Fidemraizer Feb 09 '11 at 15:10
  • 1
    Maybe I'm not being clear enough in what I am thinking :). A simpler example would be `File.ReadAllText` and reading in a short sample CSV file. In a test one could use Moles to not hit the actual file system and redirect it to a predefined text array for testing purposes. How would this not be an appropriate use of code detouring in unit tests? It seems more disadvantageous to wrap the File object just for the sake of getting to the `ReadAllText` method. I mean no disrespect with debating with you. I'm trying to get a better understanding of when best to use moles and when not too. – Matt Feb 09 '11 at 15:20
  • 3
    I'm not really convinced by your list of the three advantages of IoC. #1 Don't understand, Matt would use Moles to completely avoid the underlying layers. #2 If Matt doesn't need plugability then it's just unnecessary overhead. #3. This is also true if you use Moles. – MarkJ Feb 09 '11 at 17:05
1

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.
Gishu
  • 134,492
  • 47
  • 225
  • 308
  • +1 This is helpful. Regarding Moles and with other test runners (say NUnit, etc). Yes, moles will work with those. The documentation for Moles shows one how to do this. The research team provides the needed NUnit (or other framework) extensions. – Matt Feb 10 '11 at 13:13