views:

93

answers:

6

It's really common for me to make convenience overloads for methods. Here's an example of something I might do:

public void Encode(string value) {
    Encode(value, DefaultEncoding);
}

public void Encode(string value, Encoding encoding) { 
    // ... 
}

I'm starting to pay more attention to unit testing, and testing methods like this introduces some obstacles I'm not sure I trust myself to approach alone. The first and most important problem is whether I should duplicate tests for both overloads. For example, both methods should throw ArgumentNullException if value is null; is it more correct to recognize there could be different logic and write two tests or is it better to assume that the convenience overloads have no logic of their own?

I've also got a secondary problem. My naming scheme is the same as Roy Osherove's: "MemberName_State_ExpectedResult". If I duplicate tests, then I have clashing names without introducing some oddball naming convention. How do you handle this if you duplicate tests?

+1  A: 

I frequently don't test the convenience methods to the same level of detail as the core method they call. For example your ArgumentNullException case. I wouldn't test it twice. My feeling is that unit testing is white box testing. I'm allowed to know the implementation.

Of course this could mean that I'm going to get burned when I go and refactor later and add functionality to the convenience method. But I'm pretty decent at doing TDD (not a full zealot as you can tell from the first paragraph). So I think I would most likely write a test for that new functionality and cover it then.

I'm not claiming it's more or less correct. It's just what I do.

Mike Two
A: 

If all your constructors call the fully-specified constructor, I would break the tests into
1) your current unit test, which must be something like
ConstructorShouldDoThisAndThat()
2) specialized unit tests for the overloads, specifying what default the overload uses, like ConstructorWithNoEncodingShouldSetEncodingToDefaultEncoding()

Mathias
A: 

Avoid the issue (somewhat) by making the convenience overloads extension methods.

Dan
Extension methods are a shiny hammer, but some things still aren't nails. I would take S Lott's advice and just slightly change the naming convention.
Doug McClean
+1  A: 

" write two tests or is it better to assume that the convenience overloads have no logic of their own?"

Umm.... Your tests aren't defined by "assumptions". They're defined by the design of the class you're testing.

You don't do anything based on "assumptions".

If the convenience function is actually a convenience function, it must do the same thing and you must write a test that demonstrates that both variant methods actually do the same thing.

If "there could be different logic" (1) it isn't really a convenience function and (2) you must write a test that demonstrates that both variant methods actually do the correct thing (which may be the same with different logic, or may be different, I can't tell from the question.)

"MemberName_State_ExpectedResult. If I duplicate tests, then I have clashing names "

Avoid foolish consistency questions. If you have the same method with different signatures, then this naming convention isn't very good, is it? Sticking to it faithfully, in spite of it's problems, is a foolish consistency.

You can't trivially use this when you have methods that are distinguished only by argument signatures. So just make something up that works for all your convenience functions.

S.Lott
+1 on both accounts. For a simple way to verify the behavior of the convenience method, see my answer to this question.
Mark Seemann
+1  A: 

I think the answer is simpler then you think: do you care whether the overloaded methods work or not? If you care that they work, how do you know for certain unless you test them?

It should take you about 15 seconds to write a test that compares the output of the overloaded function with the one that it overloads. Do it and move on.

Bryan Oakley
A: 

What I usually do is to make the 'real' method virtual. This means that I can test the convenience method using a test-specific derived class (usually created by a dynamic mock) to verify that it correctly calls the 'real' method.

If the only difference between the 'real' method and the convenience method is the use of a default value for a particular parameter, you can cover this with a single unit test and move on.

I second S.Lott's answer regarding the naming convention.

Mark Seemann