问题
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:
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
回答2:
Derive a class from ManagementException and hide the error code implementation with your own. Have your mock return this class.
回答3:
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.
回答4:
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.
来源:https://stackoverflow.com/questions/1010773/how-to-set-the-errorcode-of-a-managementexception