Reputation:
I am unit-testing private methods in C# using NUnit.
For example, my method (if public) is expected to throw an ArgumentNullException
. I can assert that the method throws an ArgumentNullException
like so: Assert.Throws<ArgumentNullException>(() => method.Call());
However, since I am invoking a private method using reflection, I would assert a TargetInvocationException
for the method that throws an ArgumentNullException
like so: Assert.Throws<TargetInvocationException>(() => methodInfo.Invoke(obj, new object[] { params }));
I would like to assert an ArgumentNullException
instead of a TargetInvocationException
for that private method so I can scan over its code and know what its expected to do rather than to debug to find out.
How would I assert for the actual exception, instead of a TargetInvocationException
?
NOTE: This question is not addressing the theory behind unit-testing public vs. private methods. My team and I have made the decision to unit-test the private methods, and whether or not that is the way to unit-test is irrelevant to this question. See the most upvoted answer on this question to understand our rationale.
Upvotes: 5
Views: 4143
Reputation:
Found my answer:
var exception = Assert.Throws<TargetInvocationException>(() => methodInfo.Invoke(obj, new object[] { params }));
Assert.IsInstanceOf<Exception>(exception.InnerException);
UPDATE
Assert.IsNotNull(exception.InnerException)
lets me know that an inner exception exists. Assert.IsInstanceOf<Exception>(exception.InnerException);
will assert any type of Exception
thrown. I agree that both ways tell us that there is an inner exception.
However.....what if I want to assert for a specific type of inner exception?
For example, if my method throws an ArgumentNullException
, then I cannot assert for that by doing Assert.IsInstanceOf<FileNotFoundException>(exception.InnerException);
Using Assert.IsNotNull
lets me know that an inner exception exists, but it does not reveal the type of the inner exception. Therefore, this is why I prefer using IsInstanceOf
in this case.
Upvotes: 9
Reputation: 4567
Are you sure that unit-testing private methods is good idea? Because sounds like it isn't.
It's better to test public method that is calling private method.
Update
What's more, despite I didn't see your code, but I think it's better to put parameters validation to public methods and test them.
Update2
Found pretty similar SO question
Update3
Approach that I was using in similar case(for mocking private
fields) was just to mark private
methods as internal
and to add InternalsVisibleTo(<assembly_with_unit_tests>)
attribute to assembly under test.
It's not good idea(actually I was against this when it was proposed in team on one of design session, because it's not right, not pure, etc), but as the result it saved a lot of time for writing unit-tests.
Upvotes: 0
Reputation: 2126
Make an extension method on Assert, i.e "ThrowsInnerException" that take a func, wrap with a Try/Catch and throw the InnerException (which, in your case, correspond to the ArgumentNullException)
Here is a piece of code (untested & doesn't compile, as I typed it without editor, but it should give you the idea)
public static class AssertExtension
{
public static void ThrowsInnerException<T>(Action action)
{
Assert.Throws<T>(delegate() {
try { action(); }
catch (Exception exc) { throw exc.InnerException; }
});
}
}
Upvotes: 3