正确设置JUnit测试名称

寻找好名字是手工软件的挑战之一。 您需要随时随地找到它们-类,方法,变量,仅举几例。 但是,什么使名字成为好名字呢? 引用Oncle Bob的话:“三件事:可读性,可读性和可读性!” 他后来通过表达的清晰,简单和密度1对其进行了定义

尽管这对我来说很有意义,但是我发现自己尤其在测试方法方面有些挣扎。 为了更好地理解我在说什么,需要知道我编写了代码测试驱动程序 。 做了一段时间之后,我逐渐将工作重点从被测单元转移到了测试本身。 这可能是因为我喜欢将一个测试用例视为一个完整的规范和质量保证,因此它非常重要2

因此,只要测试失败,理想情况下,我就能一眼就能知道哪个规范被破坏以及为什么。 实现此目的的最佳方法似乎是找到一个表达性的测试名称,因为这是报告视图中显示的第一条信息:

规格

从这个角度来看,我并不总是对这种观点所显示的内容感到满意,因此我花了一些时间进行研究,以了解哪些思想流派可能会有所帮助。 不幸的是,我发现的大多数结果都有些过时,并且-不足为奇-关于该主题的意见分歧。 这篇文章基于这些发现和一些个人经验,代表了我的看法。

根据方法或行为测试名称进行测试?

每种方法的测试形式通常都是由工具提供的,例如,在事后生成单个测试存根。 如果您的类Foo带有方法bar则生成的方法将称为testBar 。 我一直对这种开发风格或命名约定的有用性持怀疑态度,并且会像旧的JavaRanch线程中的引文一样争论:“您根本不应该将其视为测试方法,而应该将其视为测试行为。班上的 因此,我喜欢我的测试方法名称来传达预期的行为3

有趣的是,我将对那一点改变看法。 如上所述,传达“行为”的想法要求找到一个简明的名称来全面表达该“行为”。 但是随后,术语行为表示由一个动作从一个状态过渡到另一种状态,或者用BDD术语表示,例如“时给定”模式。 老实说,我认为将所有这些信息放在一个单一的名字中通常不是一个好主意4

@Test
public void
  givenIsVisibleAndEnabledWhenClickThenListenerIsNotified() {}
@Test
public void
  givenIsVisibleAndNotEnabledWhenClickThenListenerIsNotNotified() {}
@Test
public void
  givenIsNotVisibleAndEnabledWhenClickThenListenerIsNotNotified() {}

也许这只是一个口味问题,但是根据我的经验,无论我选择哪种格式样式,由于缺乏简单性和/或清晰度,这种方法通常缺乏可读性。 此外,这样的重载名称往往与注释具有相同的问题–随着内容的发展,这些名称很容易过时。 因此,我想改用BUILD-OPERATE-CHECK 5模式。 这将允许将阶段拆分为单独的子方法名称,并将其放置在单个测试中:

@Test
public void testNameHasStillToBeFound() {
  // do what is needed to match precondition
  givenIsVisibleAndEnabled();

  // execute the transition
  whenClick();

  // verify the expected outcome
  thenListenerIsNotified();
}

不幸的是,这导致我们进入了起点。 但是,如果仔细看一下上面的示例,所有方法都围绕着一个共同的分母。 它们都属于触发转换的同一动作。 在我们的例子中,点击事件。 考虑到从开发过程的角度来看,我认为一个测试用例比被测试的单元更为重要,因此可以将其解释为一种符号,以在开发中的单元中以适当的方法名称来反映操作6

因此,出于示例的考虑,假设我们有一个环绕UI控件的ClickAction 。 鉴于上述情况,引入一种称为ClickAction#execute()的方法对我们来说似乎很合适。 为简单起见,我们可以将该名称也用于表示从ClickAction默认状态到通过ClickAction#execute()控件构造的转换的测试方法:

class ClickActionTest {

  @Test
  public void execute() {
    Control control = mock( Control.class );
    ClickAction clickAction = new ClickAction( control );

    clickAction.execute();

    verify( control ).notifyListeners(...)
  }
}

为了简单起见,下一个测试名称可能只提及重要的状态信息,因为它不同于默认值并导致另一个结果:

class ClickActionTest {

  [...]

  @Test
  public void executeOnDisabledControl() {
    Control control = mock( Control.class );
    when( control.isEnabled() ).thenReturn( false );
    ClickAction clickAction = new ClickAction( control );

    clickAction.execute();

    verify( control, never() ).notifyListeners(...)
  }

  @Test
  public void executeOnInvisibleControl() {
  [...]
}

如您所见,这种方法产生了一组测试名称,从技术上讲,它们代表各种“每种方法的测试”模式-但并非出于我认为完全出于不利的原因。 考虑到上下文,我认为这种命名模式很简单,清晰并且可以表达:

仍未提及预期的测试结果。 乍一看,这似乎并不令人满意,但是从我目前的观点来看,我愿意接受这一点,这是一种合理的权衡。 尤其是在JUnit报告视图中通常还会显示测试失败的原因。 因此,可以通过提供有意义的测试失败7来解决该问题。

结论

实际上,我一段时间以来正在使用上述测试命名模式。 到目前为止,效果还不错。 特别是在像我通常使用的非常小的单元上工作时,几乎没有错误解释的空间。 但是,这种方法并不适合所有情况,有时候感觉更好,并且可读性足以提及结果。 我不会在这里谈论原则,也许我弄错了。 因此,对于任何您可能会意识到可以扩大我的观点的更详细方法的指示,我都会感到高兴。

  1. Robert C. Martin关于清洁测试,清洁代码,第9章单元测试
  2. 更糟的是:丢失被测单元或测试用例? 使用良好的测试用例,在大多数情况下,恢复单元应该很容易,但是反之亦然,您很容易错过丢失的测试用例中指定的其中一个极端用
  3. 使用JUnit的方法的命名约定,使用JUnit的方法的命名约定
  4. 为避免误解: BDD并没有提供类似的功能,并且具有自己的测试框架。 我只是在这里提到它,因为“行为”一词似乎暗示了这一点,而“ givenWhenThen”一词在有关测试名称的许多讨论中不胜枚举。 然而,你居然发现像罗伊Osherove的命名约定标记建议“UnitOfWork_StateUnderTest_ExpectedBehavior”这似乎仍然被广泛接受虽然帖子已经看到大多数的最后十年的日子
  5. Robert C. Martin,《清洁规范》,第9章,清洁测试
  6. 甚至将全部功能提取到一个单独的类中。 但是,这种情况下,在我的岗位描述更具有MoreUnit单位
  7. 这可能是一个主题,当我不得不结束时,我就这样保留它!

翻译自: https://www.javacodegeeks.com/2014/03/getting-junit-test-names-right.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值