对于普通班级来说,进行单元测试覆盖是一种好习惯

时间:2012-10-11 18:44:48

标签: c# .net unit-testing tdd bdd

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

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

我的代码覆盖率结果抱怨我没有为该课程做过任何报道

8 个答案:

答案 0 :(得分:23)

我从不为这些做过测试。 没有可以测试的行为。

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

答案 1 :(得分:11)

我从Martin Fowler找到了这篇有趣的文章:

  

测试覆盖率是查找未经测试的部分的有用工具   代码库。测试覆盖率几乎没有用作如何的数字陈述   你的考试很好。

===

同样来自Object mentor的有趣引用:

  

这可能不是强制性的,如果你的目标是100%覆盖,那么你就可以了   专注于这个目标,而不是专注于为...编写最佳测试   你的代码的行为。

答案 2 :(得分:7)

我可以接受或离开它。但是,虽然现在几乎没有明显的好处,但是如果程序员后来出现,将其转换为传统的,成员字段支持的属性,并在琐碎的情况下摸索实现,那么您将被覆盖。

但我个人实际上并不打扰,因为我的时间有限,而且我有更重要的测试要写,这将为我的成功提供更大的帮助。

答案 3 :(得分:7)

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

  • 条件
  • 操作
  • 多态性

你的班级没有这些。

问自己一个问题通常会有所帮助,“如果我用单元测试覆盖这些代码,我会得到什么?”。如果“指向一些指标”是您可以提出的唯一答案,那么为这些代码编写测试可能不值得。你将花费时间,产生额外的代码,并且什么也得不到(代码覆盖率“点”没什么价值 - 至少在这种情况下是这样)。

另外,看看这两个问题(它们都解决了类似的问题,答案主要解决一个重点 - [在大多数情况下] 总会有人为你所做的事付钱 - 知道你花时间提高评级,有人可能不会非常激动,因为这本质上就是这样):

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

答案 4 :(得分:5)

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

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

我也是这样看待:在开发测试时几乎没有任何伤害,具有一些非常可能的长期优势。现在只需几分钟就可以跟踪线下引入的问题。

答案 5 :(得分:5)

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

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

答案 6 :(得分:3)

如果您只保留数据传输对象这样的类,可以说您不需要对它们进行单元测试。但是,只要您开始添加行为,就应该编写相应的测试。

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

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

答案 7 :(得分:0)

单元测试的目的是保持代码"固定"这样,如果你改变了实施方式,那么你就知道自己没有破坏任何东西,或者如果你确实破坏了某些东西,你就会立即确切知道它是什么。

这样做的另一个好处是知道你的代码当前正在编写做你想做的事情,但这不是(或不一定)单元测试的主要目标。单元测试使您可以放心地更改代码。

如果上面这样的类没有经过单元测试,那么可以打破该类,而不会导致任何测试失败。 E.g:

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();
   }
}