Reputation: 608
I've come across a scenario that I'm not too sure how to approach.
We're trying to get our unit tests code coverage to 100%.
I've been trying to take a TDD approach to development, writing tests, making it pass, writing a failing test, adding more code to make it pass, etc.
While doing this I found that I wrote a class like this:
public enum IntervalType : int
{
Shift = 1,
PaidBreak = 2,
UnpaidBreak = 3
}
public static class QuarterFactory
{
public static IQuarter CreateQuarter(IntervalType intervalType)
{
switch (intervalType)
{
case IntervalType.Shift:
return new ShiftQuarter();
default:
throw new ArgumentException(String.Format("Unable to create a Quarter based on an IntervalType of: {0}", intervalType));
}
}
}
As coding progressed the factory expanded to this:
public static class QuarterFactory
{
public static IQuarter CreateQuarter(IntervalType intervalType)
{
switch (intervalType)
{
case IntervalType.Shift:
return new ShiftQuarter();
case IntervalType.PaidBreak:
return new PaidBreakQuarter();
case IntervalType.UnpaidBreak:
return new UnpaidBreakQuarter();
default:
throw new ArgumentException(String.Format("Unable to create a Quarter based on an IntervalType of: {0}", intervalType));
}
}
}
The first question I'm asking is:
Now that the factory fulfills the enum, do I remove the default exception for the sake of code coverage, or do I keep the exception in there as the default in the event that someone adds a new type to the enum and forgets to modify the factory?
The second question I'm asking is:
If I decided to remove the exception and default the type to be UnpaidBreakQuarter - does it make sense to default the IQuarter returned to UnpaidBreakQuarter, or would this raise the question of "why is the default the UnpaidBreakQuarter, is there a business rule for this?".
Regards,
James
Upvotes: 4
Views: 388
Reputation: 1662
You know that this sort of switch/enum combo is generally a code smell right? http://c2.com/cgi/wiki?SwitchStatementsSmell
You could refactor to use polymorphism: that would keep your 100% coverage, allow easy addition of new cases, and not need a default because the compiler would prevent it.
The two most common refactorings in this case are "Replace Type Code with Class" and the more specific "Replace Type Code with Strategy". Also "Replace Conditional with Polymorphism" is of course worth a look. http://refactoring.com/catalog/replaceConditionalWithPolymorphism.html
Upvotes: 0
Reputation: 21900
You can still get 100% code coverage. Consider the following line that compiles just fine:
QuarterFactory.CreateQuarter((IntervalType)0);
Thus, this answers the second question as well. You can get really confusing results if that would return an UnpaidBreakQuarter
.
So in short:
You can even get the exception to be raised by doing this:
QuarterFactory.CreateQuarter(0);
Upvotes: 5
Reputation: 1933
I think you should keep the default at the expense of your 100% code coverage, unless there really is a business rule for this and it has been documented. If you choose to keep it, when someone adds another value in the enum, an exception will be thrown as a "reminder" to add the case in the switch statement, which I bet is good.
Upvotes: 1
Reputation: 397
I think you should keep the default block, it's a good practice, especially for the case you already mentioned, that is if someone would modify the code in the future adding a new IntervalType.
The answer to the second question comes consequently :) Btw, using the default for a specified value because "I know that the only value left is that" is really horrible, the default value was intended exactly for exceptions or unexpected cases, or at most for the most general ones (i.e: the first not, the second not, ok, so ANY other case should be this value).
Upvotes: 2