views:

140

answers:

4

I want to handle a ManagementException exception for a specific ErrorCode only and am having trouble writing the unit test for it. Ordinarily, I would write the test so that it is something like the following:

Searcher search = MockRepository.GenerateMock<Searcher>(); 
// wrapper for ManagementObjectSearcher

...

search.Expect(s => s.Get()).Throw(new ManagementException());

...

However, this doesn't set the ErrorCode to the one that I want in particular, indeed ManagementException doesn't have a constructor which sets this value.

How can this be done?

(Note that I am using RhinoMocks as my mocking framework but I am assuming that this is framework independent; all I need to know here is how to create a ManagementException which has a specific ErrorCode value. Also I have found some references to a System.Management.ManagementException.ThrowWithExtendedInfo(ManagementStatus errorCode) method online but this doesn't appear to be publicly accessible).

+1  A: 

Derive a class from ManagementException and hide the error code implementation with your own. Have your mock return this class.

Robert
A: 

I would subclass ManagementException and in the subclass override the ErrorCode getter (if the normal protection levels stop you from doing that, maybe introspection can get you closer). Any code that handles ManagementException but has never heard about your specific subclass should handle your subclass "as if" it was the ManagementException that you're trying to simulate for testing purposes, after all.

Edit: it's conceivable that ErrorCode just cannot be overridden (I hate languages that are SO rigid that they can stop testing in this way, but cannot deny they exist;-). In this case, Dependency Injection can still save you -- DI is one of my favorite patterns for testing.

DI's purpose in testing is to decouple the code under test from rigid assumptions that would inhibit testability -- and that's just what we have here, albeit in an unusual form. Your code under test currently does, say, x.ErrorCode to obtain the error code of exception x. Very well, it must then do, instead, getErrorCode(x) where getErrorCode is a delegate which normally just does return x.ErrorCode; and it must have a setter for the getErrorCode delegate, so that for testing purposes, you can change it to a delegate which does return 23 (or whatever error-code value you want to simulate for testing).

Details can vary, but dependency injection can (among other things) help compensate for some kinds of excessive rigidity in objects you inevitably get from the system (or from other libraries &c that you cannot directly modify), as in this example.

Alex Martelli
That's what RhinoMocks would do if I were to mock a ManagementException. However ErrorCode is not an override-ble property. Could you elaborate on what you mean by introspection?
jpoh
In native .NET, that's http://msdn.microsoft.com/en-us/library/system.reflection.aspx -- but if there are properties that are non-overridable in this way, dependency injection is your last, best hope -- let me edit my answer accordingly to explain.
Alex Martelli
A: 

Have a very simple and small method or class which catches that exception and gets the error code out of it, and then passes that on to the real class that does the work. Under test, replace that code with directly passing to the real class what you would pass when you get that error code.

The most obvious way is to subclass the exception, but if that doesn't work, then code which catches it, and immediately throws your own exception that does allow you to expose that code would be another option.

Yishai
+3  A: 

The least effort to get over this hurdle would be a static helper / utility method that uses reflection to hack-slot in the required error code. Using the most excellent Reflector, I see there is a private "errorCode" field, which is only set via internal ctors defined in ManagementException. So :)

public static class EncapsulationBreaker
   {
      public static ManagementException GetManagementExceptionWithSpecificErrorCode(ManagementStatus statusToBeStuffed)
      {
         var exception = new ManagementException();
         var fieldInfo = exception.GetType().GetField("errorCode", 
            BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.DeclaredOnly);
         fieldInfo.SetValue(exception, statusToBeStuffed);
         return exception;
      }
   }

Verified that it works

[Test]
      public void TestGetExceptionWithSpecifiedErrorCode()
      {
         var e = EncapsulationBreaker.GetManagementExceptionWithSpecificErrorCode(ManagementStatus.BufferTooSmall);
         Assert.AreEqual(ManagementStatus.BufferTooSmall, e.ErrorCode);
      }

Although I generally frown upon reflection in tests, this is one of the rare cases where it is needed / useful.
HTH

Gishu
Exactly what I need. Thanks!
jpoh