编程和单元测试最佳实践

4
我遇到了一个场景,我不太确定如何处理。
我们正在努力将单元测试代码覆盖率提高到100%。
我一直试图采用TDD的开发方法,编写测试,使其通过,编写失败的测试,添加更多代码以使其通过等。
在这样做的过程中,我发现我写了一个像这样的类:
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));
       }
    }
 }

随着编码的进行,工厂扩展到了这个规模:
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));
        }
    }
}
我要问的第一个问题是:
现在工厂已经实现了枚举,我是否应该删除默认异常以便进行代码覆盖率测试,还是保留异常作为默认值,以防有人添加新类型到枚举中时忘记修改工厂?

我要问的第二个问题是:
如果我决定删除异常并将类型默认为UnpaidBreakQuarter,那么将IQuarter默认设置为UnpaidBreakQuarter是否合理,或者这会引发“为什么默认是UnpaidBreakQuarter,这里是否存在业务规则?”的问题。

此致敬礼,
James

4个回答

5
您仍然可以获得100%的代码覆盖率。考虑以下编译正常的行:

QuarterFactory.CreateQuarter((IntervalType)0);

因此,这也回答了第二个问题。如果返回 UnpaidBreakQuarter ,你可能会得到非常混乱的结果。
简而言之:
  1. 保持默认值,单元测试如果传递了意外的参数(例如上面的行),则引发异常
  2. 不要这样做。如果有人没有指定他们想要的内容,他们不会期望它被返回
即使通过这种方式,您也可以引发异常:
QuarterFactory.CreateQuarter(0);

2

我认为你应该保留默认块,这是一个好的实践,特别是对于你已经提到的情况,即如果将来有人修改代码添加新的IntervalType。

第二个问题的答案随之而来:)顺便说一句,因为“我知道唯一剩下的值是那个”,所以将默认值用于指定值真的很糟糕, 默认值的目的正是为了异常或意外情况,或者最多适用于最常见的情况(即:第一个不是,第二个不是,好吧,那么任何其他情况都应该是这个值)。


1

我认为你应该保持默认值,即使这会牺牲你的100%代码覆盖率,除非真的有业务规则并已经记录下来。如果你选择保留它,当有人在枚举中添加另一个值时,将抛出异常作为“提醒”在switch语句中添加相应的case,我敢打赌这是很好的。


0
您知道这种switch/enum组合通常是代码异味吗? http://c2.com/cgi/wiki?SwitchStatementsSmell 您可以重构成使用多态性:这将保持100%的覆盖率,允许轻松添加新的用例,并且不需要默认值,因为编译器会防止它。
在这种情况下最常见的两种重构是“使用类替换类型代码”和更具体的“使用策略替换类型代码”。当然,“使用多态性替换条件语句”也值得一看。 http://refactoring.com/catalog/replaceConditionalWithPolymorphism.html

嗨,卡尔,我对代码异味的理解是如果相同的switch语句在代码中的不同位置使用。然而,这个switch语句被封装在一个工厂内。请注意,该工厂正在使用多态性。 - 在代码的某个点上,您需要创建一个具体实例,无论是基于数据库中的整数(在这种情况下是枚举),还是不同的具体实现(其实例化需要在某个地方进行条件语句)。 - Zack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接