编程,单元测试最佳实践

本文关键字:最佳 单元测试 编程 | 更新日期: 2023-09-27 18:10:15

我遇到了一个我不太确定如何处理的场景。

我们正在努力使我们的单元测试代码覆盖率达到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,是否有业务规则?"的问题。

问候,詹姆斯

编程,单元测试最佳实践

您仍然可以获得100%的代码覆盖率。考虑下面这行编译得很好:

QuarterFactory.CreateQuarter((IntervalType)0);

因此,这也回答了第二个问题。如果返回一个UnpaidBreakQuarter,你会得到非常令人困惑的结果。

简而言之:

  1. 保持默认的单元测试,如果通过则抛出异常一些意想不到的东西(就像上面那行)
  2. 别这么做。如果有人没有明确说明他们想要这个,他们不会期望它被退回

你甚至可以通过这样做来引发异常:

QuarterFactory.CreateQuarter(0);

我认为你应该保持默认块,这是一个很好的做法,特别是对于你已经提到的情况,也就是说,如果有人在将来修改代码添加一个新的IntervalType。

第二个问题的答案随之而来:)顺便说一句,对指定值使用默认值,因为"我知道唯一剩下的值是that"真的很可怕,默认值是专门用于异常或意外情况的,或者最多用于最一般的情况(即:第一个not,第二个not,好,所以任何其他情况都应该是这个值)。

我认为您应该以100%的代码覆盖率为代价保持默认值,除非确实有这样的业务规则并且已经记录下来。如果您选择保留它,当有人在enum中添加另一个值时,将抛出一个异常作为"提醒"在switch语句中添加case,我打赌这是好的。

您知道这种开关/枚举组合通常是一种代码气味,对吗?http://c2.com/cgi/wiki?SwitchStatementsSmell

您可以使用多态性进行重构:这将保持100%的覆盖率,允许轻松添加新用例,并且不需要默认值,因为编译器会阻止它。

在这种情况下最常见的重构是"用类替换类型代码"和更具体的"用策略替换类型代码"。此外,"用多态性替换条件"当然值得一看。http://refactoring.com/catalog/replaceConditionalWithPolymorphism.html