单元测试:为啥预期的参数总是在相等测试中排在第一位?

Posted

技术标签:

【中文标题】单元测试:为啥预期的参数总是在相等测试中排在第一位?【英文标题】:Unit testing: why is the expected argument always first in equality tests?单元测试:为什么预期的参数总是在相等测试中排在第一位? 【发布时间】:2012-02-17 15:45:42 【问题描述】:

为什么每个单元测试框架(据我所知)都要求相等测试中的期望值始终是第一个参数:

Assert.AreEqual(42, Util.GetAnswerToLifeTheUniverseAndEverything());

assertEquals(42, Util.GetAnswerToLifeTheUniverseAndEverything());

等等

我现在已经很习惯了,但是我尝试教授单元测试的每个编码员都会犯一个颠倒参数的错误,我完全理解这一点。谷歌没有帮助,也许这里的核心单元测试人员之一知道答案?

【问题讨论】:

您是否查看过测试失败时产生的默认错误消息?这就是为什么。或者你在问为什么这些信息是这样写的?您是在询问担心if( 23 = a )if( a = 23 ) 的C“标准”吗?你问的是这个吗? 可能只是为了方便,断言表达式可能比较复杂,所以把预期的结果放在第一位很容易找到。 @S.Lott:我知道为什么我需要按顺序通过它们,我想知道为什么大多数单元测试接口都是这样编写的。 您是否在询问担心if( 23 = a )if( a = 23 ) 的C“标准”以及这在单元测试中如何体现? @S.Lott:这在某处有记录吗?我同意,这听起来很合理,但我还没有看到任何证据。 【参考方案1】:

似乎大多数早期框架在 actual 之前使用 expected(但出于某种未知原因,可能是掷骰子?)。然而,随着编程语言的发展,以及代码流畅度的提高,这一顺序发生了逆转。大多数流畅的界面通常会尝试模仿自然语言,单元测试框架也不例外。

在断言中,我们要确保某些对象匹配某些条件。这是自然语言形式,就好像你要解释你可能会说的测试代码

“在这个测试中,我确保计算的值等于 5”

而不是

“在这个测试中,我确保 5 等于计算值”。

差异可能不大,但让我们进一步推动它。考虑一下:

Assert.That(Roses, Are(Red));

听起来差不多。现在:

Assert.That(Red, Are(Roses));

嗯..?如果有人告诉你玫瑰是红色的,你可能不会太惊讶。反过来说,red are roses,提出了可疑的问题。 Yoda,有人吗?

尤达提出了一个重要的观点 - 颠倒的顺序迫使你think。

当您的断言更复杂时,它会变得更加不自然

Assert.That(Forest, Has.MoreThan(15, Trees));

你会如何扭转这一局面? 森林拥有超过 15 棵树

这种说法(流畅性是修改的驱动因素)以某种方式反映在 NUnit 所经历的变化中 - 最初 (Assert.AreEqual) 它在 实际 之前使用 预期 >(旧样式)。 Fluent 扩展(或使用 NUnit 的术语,基于约束 - Assert.That)颠倒了这个顺序。

【讨论】:

不相信硅计算机语言正在与肉类计算机语言融合。 y = f(x) 之类的代码不太可能朝着“将 x 传递给 f 并将返回值存储在 y 中”的方向变形。【参考方案2】:

我认为它现在只是一个约定,正如您所说,它被“每个单元测试框架(我知道)”采用。如果您正在使用一个框架,那么切换到另一个使用相反约定的框架会很烦人。所以(例如,如果你正在编写一个新的单元测试框架)你最好也遵循现有的约定。 我相信这来自一些开发人员更喜欢编写他们的相等测试的方式:

if (4 == myVar)

为避免任何不必要的分配,错误地写一个“=”而不是“==”。在这种情况下,编译器会捕捉到这个错误,你会避免很多试图修复奇怪的运行时错误的麻烦。

【讨论】:

【参考方案3】:

没有人知道,它是永无止境的混乱的根源。然而,并非所有框架都遵循这种模式(更加混乱):

    FEST-Assert 使用正常顺序:

    assertThat(Util.GetAnswerToLifeTheUniverseAndEverything()).isEqualTo(42);
    

    Hamcrest

    assertThat(Util.GetAnswerToLifeTheUniverseAndEverything(), equalTo(42))
    

    ScalaTest 并没有真正区分:

    Util.GetAnswerToLifeTheUniverseAndEverything() should equal (42)
    

【讨论】:

【参考方案4】:

我不知道,但我参与了一些关于一般相等性测试的参数顺序的热烈讨论。

很多人认为

if (42 == answer) 
  doSomething();

优于

if (answer == 42) 
  doSomething();

在基于 C 的语言中。这样做的原因是,如果你不小心放了一个等号:

if (42 = answer) 
  doSomething();

会给你一个编译器错误,但是

if (answer = 42) 
  doSomething();

可能不会,并且肯定会引入一个可能难以追踪的错误。那么谁知道呢,也许建立单元测试框架的人习惯于以这种方式考虑相等性测试——或者他们正在复制已经以这种方式建立的其他单元测试框架。

【讨论】:

我认为这是作为理由,当参数将评估顺序与参数顺序匹配时,实际相等性检查的顺序可能更容易观察。【参考方案5】:

我认为这是因为 JUnit 是大多数单元测试框架的先驱(不是说它是第一个单元测试框架,而是它引发了单元测试的爆炸式增长)。由于 JUnit 是这样做的,所有后续的框架都复制了这种形式,并成为了一种约定。

为什么 JUnit 会那样做?我不知道,问肯特贝克!

【讨论】:

【参考方案6】:

我对此的看法是避免任何例外,例如:42.equals(null) vs null.equals(42)

预期为 42 null 是实际的

【讨论】:

可能有算法在给定42时输出null,或者在给定null时输出42。【参考方案7】:

好吧,他们必须选择一个约定。如果你想扭转它,试试 Hamcrest 匹配器。它们旨在帮助提高可读性。这是一个基本示例:

import org.junit.Test;
import static org.junit.Assert.assertThat;
import static org.hamcrest.core.Is.is;

public HamcrestTest
   @Test
   public void matcherShouldWork()
       assertThat(   Math.pow( 2, 3 ),  is( 8 )  );
   

【讨论】:

我不知道你在 py.test 中所做的那样比普通的 assert 8 == Math.pow(2, 3) 更具可读性! 1) 你不能混淆期望值与实际值的顺序 2) 你可以扩展匹配器,这样你就可以向 Hamcrest 添加更多“动词”,就像你可以添加一样assertThat( Math.pow( 2, 3 ), is(relativelyPrimeTo( 49 )) );【参考方案8】:

当然,将期望值放在首位是合乎逻辑的,因为它是第一个已知值。

在手动测试的背景下考虑它。手动测试将写入预期值,然后记录实际值。

【讨论】:

“逻辑意义”?短语“我期待‘生命的意义是什么?’这个问题的答案。 to equal 42" 按逻辑组织,合乎逻辑。 是的,句子排列正确。我更多地谈论期望的概念。期望首先出现在逻辑上。看看谷歌对期望的定义:“强烈相信某事会发生或会发生”。 发生。即它会发生在 预期设定之后。 不,它没有。

以上是关于单元测试:为啥预期的参数总是在相等测试中排在第一位?的主要内容,如果未能解决你的问题,请参考以下文章

关于自动化测试的误区

将预期结果基于单元测试中的实际结果是不好的做法吗?

使用 Jest 的不可理解的单元测试效果错误

转单元测试接口测试功能测试的区别

如何从单元测试中排除名称中带有“Test”的帮助项目?

以最快的方式重新排序字母以按字典顺序排在第一位