user5398447
user5398447

Reputation:

Asserting Exceptions for Private Methods

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

Answers (3)

user5398447
user5398447

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

alex.b
alex.b

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

Fabske
Fabske

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

Related Questions