20

这是一个完全没有行为的类的示例。所以问题是我应该为它做单元测试覆盖,因为我认为它是不必要的,因为它确实有任何行为。

public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Address Address { get; set; }
}

我的代码覆盖率结果抱怨我没有对该类进行任何覆盖

4

8 回答 8

23

我从不为这些编写测试。 没有要测试的行为。

如果有任何重要的行为代码(也许是验证代码),那么我会为它编写一个测试。

于 2012-10-11T18:46:50.673 回答
11

我发现Martin Fowler的这篇有趣的文章:

测试覆盖率是查找代码库中未测试部分的有用工具。测试覆盖率作为测试有多好的数字声明几乎没有用。

===

还有来自对象导师的这个有趣的引用:

这可能不是强制性的,如果您的目标是 100% 的覆盖率,您将专注于该目标,而不是专注于为代码的行为编写最佳测试。

于 2012-10-11T18:51:20.827 回答
7

我可以接受,也可以离开。但是,虽然现在几乎没有明显的好处,但如果后来有程序员出现,将它们转换为传统的、成员字段支持的属性,并在微不足道的情况下搞砸了实现,你就会被覆盖。

但我个人实际上不会打扰,因为我的时间有限,而且我还有更重要的测试要编写,这将为我带来更大的收益。

于 2012-10-11T18:47:55.413 回答
7

Kent Beck 在他的“示例测试驱动开发”中列出了您应该测试的内容:

  • 条件句
  • 循环
  • 操作
  • 多态性

你的班级没有这些。

问自己一个问题通常会有所帮助,“如果我用单元测试覆盖这段代码,我会得到什么?” . 如果“指向某些指标”是您能想出的唯一答案,那么为此类代码编写测试可能不值得。您将花费时间,产生额外的代码,却一无所获(代码覆盖“点”没有什么价值——至少在这种情况下)。

另外,看看这两个问题(他们都解决了类似的问题,并且答案主要解决了一个重要问题 - [在大多数情况下]总是有人为你的工作付钱 - 有人可能不会很高兴认识你花时间提高收视率,因为它本质上就是这样):

定论?我同意其他人说你不应该测试这样的课程。

于 2012-10-11T19:33:06.587 回答
5

这不是一个坏主意...例如,您开发了一个用于将来存储此数据的后端(例如,存储到数据库中)。您可以get; set;用其他自定义功能替换通用调用。进行单元测试可确保您的代码始终得到执行,并且在转换过程中不会引入任何错误。

即使是一个非常简单的测试来检查您是否可以创建对象、设置字段并读回相同的值,这表明您正在以预期的行为执行代码。即使这一切对您来说都是显而易见的,该代码的未来开发人员将有示例可以借鉴并确保任何更改都符合您的原始设计目标。

我也是这样看待的:开发具有一些非常可能的长期优势的测试几乎没有害处。现在只需要几分钟而不是跟踪引入的问题。

于 2012-10-11T18:47:34.493 回答
5

替代方案:您没有覆盖这个特定类的事实暗示有一些代码使用这个类并且该代码也没有测试覆盖率。我会先解决这个问题,然后看看是否需要更多测试。

至于测试这种纯数据对象:如果该对象是库/单元外部合同的一部分,我肯定会添加显式测试。您承诺此数据将由您的代码返回/使用。如果没有测试验证您可以更改代码的内部结构并更改此类,那么您将违反承诺,没有什么可以阻止您。

于 2012-10-11T18:58:38.973 回答
3

如果您只是将此类类保留为数据传输对象,则可能会争辩说您不需要对它们进行单元测试。但是,一旦开始添加行为,就应该编写相应的测试。

查看可能作为构建过程的一部分返回的代码覆盖率也可能有助于发现任何缺失的测试。

作为一个简短的回答,我会说“是”,对于普通课程,您可以在没有得到他们的报道的情况下逃脱。

于 2012-10-11T18:48:08.190 回答
0

单元测试的重点是保持你的代码“固定”,这样如果你改变实现,你就会知道你没有破坏任何东西,或者如果你确实破坏了某些东西,你会立即确切地知道你破坏了什么。

这样做的一个附带好处是知道您当前编写的代码可以执行您希望它执行的操作,但这不是(或不一定)单元测试的主要目标。单元测试使您可以放心地更改代码。

如果上述类没有经过单元测试,则可以破坏该类而不会导致任何测试失败。例如:

public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get { return "Smith"; } set { FirstName = value; } }
    public Address Address { get; set; }
}

通过单元测试,您可以知道在您的整个代码库中没有发生过类似的事情。(这个具体的例子有可能吗?也许没有。但这样事情是很有可能的,而且基本上是不可避免的。)

只需四个简单的单元测试,您就可以“修复”此代码,因为您知道如果没有测试失败,它不可能改变它的行为*:

[TestMethod]
public void TestId() {
   var emp = new Employee { Id = 3 };
   Assert.AreEqual(3, emp.Id);
}

[TestMethod]
public void TestFirstName() {
   var emp = new Employee { FirstName = "asdf" };
   Assert.AreEqual("asdf", emp.FirstName);
}

[TestMethod]
public void TestLastName() {
   var emp = new Employee { LastName = "asdf" };
   Assert.AreEqual("asdf", emp.LastName);
}

[TestMethod]
public void TestAddress() {
   var address = new Address();
   var emp = new Employee { Address = address };
   Assert.AreEqual(address, emp.Address);
}

(*也许除了模仿您用来测试的值的损坏实现的侧面情况)

我不会在这里实现它,但您也可以使用反射来简化编写这些类型的测试。例如,您可以相对容易地构建一些允许您执行以下操作的东西:

[TestClass]
public sealed TestEmployee {
   [TestMethod]
   public void TestSimpleProperties() {
      Assert.IsTrue(
         SimplePropertyTester.Create(
            new SimplePropertyTestCollection<Employee> {
               { emp => emp.Id, 3 },
               { emp => emp.FirstName, "asdf" },
               { emp => emp.LastName, "1234" },
               { emp => emp.Address, new Address() }
            }
         ).Test()
      );
   }
}

您失去了将每个属性都放在自己的测试中的好处,但是您获得了非常容易修改测试集的能力。

您也可以为每个单元测试构建一个单独的测试器,然后将测试分开,但这与手动编写它们并没有太大区别,并且您最终编写了基本上完全相同的代码行一遍又一遍。

您可以做的一件事是将Console.WriteLine语句放入Test()方法中,然后您应该能够查看一些文本输出以更快地查明问题。

编辑:实际上我现在已经让这些看起来像下面这样:

[TestClass]
public sealed TestEmployee {
   [TestMethod]
   public void TestSimpleProperties() {
      var factory = SimplePropertyTestFactory.Create<Employee>();
      new SimplePropertyTestCollection<Employee> {
         factory.IntTest(emp => emp.ID),
         factory.StringTest(emp => emp.FirstName),
         factory.StringTest(emp => emp.LastName),
         factory.ReferenceTest(emp => emp.Address)
      }.Test();
   }
}
于 2015-09-08T21:06:59.197 回答