49

您是否应该对类的简单属性进行单元测试,断言设置和检索值?或者这真的只是对语言进行单元测试?

例子

public string ConnectionString { get; set; }

测试

public void TestConnectionString()
{
    var c = new MyClass();
    c.ConnectionString = "value";

    Assert.Equal(c.ConnectionString, "value");
}

我想我看不到其中的价值。

4

8 回答 8

58

我建议你绝对应该。

  • 什么是今天的自动财产,明天可能最终会有一个反对它的支持领域,而不是你……

  • “您只是在测试编译器或框架”的论点有点像稻草人恕我直言。从调用者的角度来看,测试自动属性时所做的是测试类的公共“接口”。调用者不知道这是具有框架生成的后备存储的自动属性,还是 getter/setter 中有一百万行复杂代码。因此,调用者正在测试该属性所暗示的合同——如果你将 X 放入盒子中,你可以稍后取回 X。

  • 因此,我们应该包含一个测试,因为我们正在测试我们自己代码的行为,而不是编译器的行为。

  • 像这样的测试可能需要一分钟的时间来编写,所以它并不十分繁琐;并且您可以轻松地创建一个 T4 模板,该模板将通过一些反思为您自动生成这些测试。我现在实际上正在开发这样一个工具来为我们的团队节省一些苦差事

  • 如果你正在做纯 TDD,那么它会迫使你停下来考虑一下拥有汽车公共财产是否是最好的事情(提示:通常不是!)

  • 难道您不想进行前期回归测试,以便当 FNG 执行以下操作时:


//24-SEP-2013::FNG - put backing field for ConnectionString as we're now doing constructor injection of it
public string ConnectionString
{
   {get { return _connectionString; } }
   {set {_connectionString="foo"; } }//FNG: I'll change this later on, I'm in a hurry
}

///snip

public MyDBClass(string connectionString)
{
   ConnectionString=connectionString;
}

马上就知道他们弄坏了东西?

如果上面的内容似乎是针对一个简单的字符串属性而设计的,那么我个人曾见过这样一种情况,即自动属性被认为自己非常聪明并想将其从实例成员更改为静态类成员的包装器的人重构(表示发生的数据库连接,更改的原因并不重要)。

当然,这个非常聪明的人完全忘记告诉其他人他们需要调用一个魔法函数来初始化这个静态成员。

这导致应用程序编译并发送给客户,因此它立即失败。没什么大不了的,但它花费了几个小时的支持时间==金钱.... 顺便说一句,那个布偶就是我!

编辑:根据该线程上的各种对话,我想指出对读写属性的测试非常简单:

[TestMethod]
public void PropertyFoo_StoresCorrectly()
{
   var sut = new MyClass();
   sut.Foo = "hello";
   Assert.AreEqual("hello", sut.Foo, "Oops...");
}

编辑:你甚至可以按照 Mark Seeman 的Autofixture在一行中完成

我认为,如果你发现你有如此多的公共属性,以至于每写 3 行像上面这样都是一件苦差事,那么你应该质疑你的设计;如果您依靠另一个测试来表明此属性存在问题,那么要么

  • 测试实际上是在测试这个属性,或者
  • 您将花费更多时间来验证其他测试是否因为属性不正确(通过调试器等)而失败,而不是您在上面的代码中输入所花费的时间
  • 如果其他一些测试允许您立即判断该属性有问题,那么它不是单元测试!

编辑(再次!):正如评论中指出的那样,生成的 DTO 模型之类的东西可能是上述情况的例外,因为它们只是用于将数据转移到其他地方的愚蠢的旧桶,而且因为工具创建了它们,测试它们通常是没有意义的。

/编辑

最终,“视情况而定”可能是真正的答案,但需要注意的是,最好的“默认”处置是“总是这样做”的方法,但在知情的情况下逐案处理除外。

于 2013-09-24T07:58:54.267 回答
42

一般来说,没有。应使用单元测试来测试单元的功能。您应该在一个类上对方法进行单元测试,而不是对单个的自动属性进行单元测试(除非您使用自定义行为覆盖 getter 或 setter)。

您知道,如果您获得正确的语法和设置器值,那么将字符串值分配给自动字符串属性将起作用,因为这是语言规范的一部分。如果你不这样做,那么你会得到一个运行时错误来指出你的缺陷。

单元测试应该设计为测试代码中的逻辑错误,而不是编译器无论如何都会捕获的错误。

编辑:根据我与该问题已接受答案的作者的对话,我想添加以下内容。

我可以理解 TDD 纯粹主义者会说您需要测试自动属性。但是,作为一名业务应用程序开发人员,我需要权衡一下,与解决可能由不测试。根据个人经验,大多数因更改琐碎代码而产生的错误在 99% 的情况下都是微不足道的。出于这个原因,我会说仅对非语言规范功能进行单元测试的好处大于坏处。

如果您在使用 TDD 方法的快节奏业务环境中工作,那么该团队的部分工作流程应该是只测试需要测试的代码,基本上是任何自定义代码。如果有人进入您的班级并更改自动属性的行为,那么他们有责任在此时为其设置单元测试。

于 2013-09-23T19:57:49.003 回答
4

我不得不说不。如果这不起作用,你有更大的问题。我知道我没有。现在有些人会争辩说,例如,如果属性被删除,单独使用代码将确保测试失败。但是我认为如果删除了该属性,单元测试代码将在重构中被删除,所以没关系。

于 2013-09-23T19:56:27.847 回答
4

您是否遵守严格的 TDD 实践?

如果是,那么你绝对应该在公共 getter 和 setter 上编写测试,否则你怎么知道你是否正确地实现了它们?

如果不是,您可能仍然应该编写测试。尽管今天的实现是微不足道的,但不能保证仍然如此,并且如果没有涵盖简单 get/set 操作功能的测试,当未来对实现的更改破坏了“将属性 Foo 设置为值 Bar 的不变量时,会导致属性 Foo 返回值 Bar 的 getter”单元测试将继续通过。测试本身也很容易实现,但要防止未来发生变化。

于 2013-09-23T20:22:18.297 回答
2

我看到的方式是,有多少单元测试(或一般测试)取决于您对代码按设计工作的信心以及它在未来发生故障的可能性有多大。

如果您对代码破坏的信心较低(可能是由于代码被外包并且逐行检查的成本很高),那么单元测试属性可能是合适的。

一旦你可以做的是编写一个帮助类,它可以检查类的所有获取/设置属性,以测试它们是否仍然按设计运行。

于 2013-09-23T20:28:36.400 回答
1

除非属性执行任何其他类型的逻辑,否则不会。

是的,这就像对语言进行单元测试。否则,测试简单的自动实现的属性是完全没有意义的。

于 2013-09-23T19:59:19.987 回答
1

根据The Art of Unit Testing With Examples in .NET一书,单元测试不涵盖任何类型的代码,它专注于逻辑代码。那么,什么是逻辑代码

逻辑代码是任何包含某种逻辑的代码,尽管它可能很小。如果它具有以下一项或多项,则它是逻辑代码:IF 语句、循环、switch 或 case 语句、计算或任何其他类型的决策代码。

一个简单的 getter/setter 是否包含任何逻辑?答案是:

属性(Java 中的 getter/setter)是很好的代码示例,通常不包含任何逻辑,因此不需要测试。但请注意:在属性中添加任何检查后,您需要确保正在测试逻辑。

于 2015-09-03T11:01:08.460 回答
1

我的回答来自前测试经理的观点,现在是开发经理(负责及时和质量交付软件)的观点。我看到人们在提到实用主义。实用主义不是一个好的顾问,因为它可能会伴随着懒惰和/或时间压力。它可能会导致你走错路。如果您提到实用主义,您必须小心地将您的意图保持在专业和常识的轨道上。接受答案需要谦虚,因为它们可能不是您想听到的。

在我看来,接下来重要的是:

  • 你应该尽早发现缺陷。这样做你必须应用适当的测试策略。如果它正在测试属性,那么您必须测试属性。如果没有,那就不要这样做。两者都是有代价的。
  • 您的测试应该简单快速。在构建时测试的代码的较大部分(单元、集成等)越好。
  • 您应该进行根本原因分析以回答以下问题,并使您的组织免受当前类型的错误的影响。别担心,另一种缺陷会出现,总会有教训要吸取。
    • 根本原因是什么?
    • 下次如何避免?
  • 另一个方面是创建/维护测试的成本。不测试属性,因为它们维护起来很无聊和/或你有数百个属性是荒谬的。您应该创建/应用工具来完成木刻工作而不是人类。一般来说,您总是必须增强您的环境以提高效率。
  • 其他人说的不是好顾问——不管是马丁·福勒还是西曼说的——他们所处的环境我很确定与你所处的环境不同。你必须利用你的知识和经验来设置什么对您的项目以及如何使其更好。如果您因为人们所说的而应用事物,那么您甚至没有经过深思熟虑就受到尊重,您会发现自己陷入困境。我并不是说您不需要建议和/或其他人的帮助或意见,您必须运用常识来应用它们。
  • TDD 没有回答两个重要问题,但是,BDD 确实为您提供了以下问题的答案。但是,如果您只关注一个,您将无法及时和高质量地交付。所以无论你是否是纯粹的 TDD 人都没关系。
    • 必须测试什么?(它说一切都必须经过测试 - 我认为错误的答案)
    • 什么时候必须完成测试?

总而言之,没有好的答案。只是您必须回答的其他问题才能获得能够决定是否需要它的临时点。

于 2016-12-03T14:53:48.090 回答