为什么assertEquals()参数的顺序为(预期的,实际的)?


76

为什么这么多assertEquals()或类似的函数将期望值作为第一个参数,而将实际值作为第二个参数?
这对我来说似乎是违反直觉的,那么这个异常的命令是否有特定的原因?


8
这就是为什么我通常最终使用匹配器,例如assertThat(actual,is(expected))的原因,我发现它非常容易阅读
JonathanC

2
您确定订单确实是吗?docs并没有为其assertEqual本身指定标准docs.python.org/2/library/…,并且浏览该页面显示该顺序在unittest模块本身内不一致。但是这个Python问题实际上暗示着(实际上,是预期的)标准:bugs.python.org/issue10573
Michael Scheper

1
另外,请注意,assertEquals它已被弃用-使用assertEqual(即使至少在2.7中,它实际上并未指示预期的参数和实际的参数)
Michael Scheper 2015年

2
@warvariuc:看起来他们已经从“预期”和“实际”变为“第一”和“第二”。我认为这会使单元测试变得更加模棱两可,并使测试输出更加难以理解。知道为什么他们要这样改变吗?
Michael Scheper

4
@warvariuc:我对“实际”和“预期”的顺序没有任何强烈的感触,只要它是一致的,而且最重要的是,这些参数应明确命名。但是在链接到的差异文件中,参数似乎已重命名为“ first”和“ second”。这些不仅不清楚,而且实际上毫无意义!使用这些名称,我无法确定失败消息是否self.assertEqual(ltuae, 42)应该是42或54。如果测试失败,我希望该消息是有用且准确的,以便可以尽快修复该错误。新的参数名称使操作更加困难。
Michael Scheper

Answers:


29

因为作者有50%的机会符合您的直觉。

由于其他过载

assertWhatever(explanation, expected, actual)

并且,作为您所知道的部分的解释与所期望的(即您所知道的,而不是实际的)相吻合,而不是您在编写代码时不知道的实际情况。


14
但是,在那种情况下,它变得有点不一致,因为在大多数语言中,必需的参数首先出现,而必需的参数最后出现。这就是为什么我会更自然地去assertWhatever(实际,预期[,说明])
约旦

3
没错,乔!而且,在条件语句中写if(s ==“ a”)而不是if(“ a” == s)更为常见(尽管有争议的是反过来说是否会更好-就通用性而言,第一个一胜)。
Benroth 2015年

3
我认为这对于50%的人口来说是不直观的……您不会说assertGreater(a, b)断言b > a是直观的……(而且我认为这就是为什么他们删除了非交换性方法的原因)
boumbh

2
如果您不小心进行了赋值=而不是进行等效性检查==,则首先放置常量是一种获取错误的方法,因为当您尝试重新分配常量时,它将抛出错误。(我想我首先看到它是在“编写可靠的代码”中得到推广的)。它创建(IMHO)可读性较低的代码(我更喜欢“结果应符合预期”样式语法),但是如果忽略了=,则会使编译器破坏您的想法。签名
Chuck van der Linden

13

回答来自肯特·贝克,苏尼特和JUnit(其中可能这个约定起源)的创造者,是:

在一行中插入一串assertEquals。首先有期望,使他们阅读更好。

但是,这与我自己的经历正好相反,以至于我不得不怀疑自己是否误解了。这是我在测试中经常看到的内容:

assertEquals(12345, user.getId());
assertEquals("kent", user.getUsername());
assertEquals("Kent Beck", user.getName());

我认为首先使用实际值会更好。这将更多的重复样板放在一起,使我们正在测试其值的方法调用对齐:

assertEquals(user.getId(), 12345);
assertEquals(user.getUsername(), "kent");
assertEquals(user.getName(), "Kent Beck");

(还有其他一些原因使我更喜欢这种顺序,但是出于这个关于为什么是另一种方式的问题,即使我不理解,肯特的推理也似乎是答案。)


7

我同意一致性是第一位的共识,但是如果您正在评估这个问题,比较字典的行为可能是一个有用的数据点。

当我在差异上看到“ +”时,我将其读为“正在测试的过程添加了它”。同样,个人喜好适用。

注意:我使用字母键并增加了字典的长度,因此为了示例的清楚,仅中间键会发生变化。其他方案显示更多混淆的差异。同样值得注意的是,assertEqual在> = 2.7和> = 3.1中使用assertDictEqual

例题

from unittest import TestCase


class DictionaryTest(TestCase):

    def test_assert_order(self):
        self.assertEqual(
            {
                'a_first_key': 'value',
                'key_number_2': 'value',
                'z_last_key': 'value',
                'first_not_second': 'value',
            },
            {
                'a_first_key': 'value',
                'key_number_2': 'value',
                'z_last_key': 'value',
                'second_not_first': 'value',
            }
        )

输出:

$ python -m unittest exl
F
======================================================================
FAIL: test_assert_order (exl.DictionaryTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "exl.py", line 18, in test_assert_order
    'second_not_first': 'value',
AssertionError: {'a_first_key': 'value', 'z_last_key': 'value', 'key_number_2': 'value', 'first_ [truncated]... != {'a_first_key': 'value', 'z_last_key': 'value', 'key_number_2': 'value', 'second [truncated]...
  {'a_first_key': 'value',
-  'first_not_second': 'value',
   'key_number_2': 'value',
+  'second_not_first': 'value',
   'z_last_key': 'value'}

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)

6

这是一个很有启发性的话题,这里也有很多非常有教育意义的答案!这是我从他们那里学到的东西:

  1. 直觉/反直觉可以被认为是主观的,因此,无论最初定义的顺序是什么,也许我们中的50%都不高兴

  2. 我个人更希望将其设计为assertEqual(actual, expected),因为鉴于assert和之间的概念相似性if,我希望它遵循的规范if actual == expect,例如if a == 1

    (PS:的确,有不同的意见会提示您以“相反的顺序”编写if语句if(1==a) {...},例如,以防止您意外遗漏一个=。但是,即使在C / C ++世界。如果您碰巧正在编写Python代码,那么您一开始就不会受到那种讨厌的错字的影响,因为if a = 1在Python中是无效的。)

  3. 实际令人信服的理由assertEqual(expect, actual)是,您语言中的unittest库可能已经遵循了该命令来生成可读的错误消息。例如,在Python中,这样做时assertEqual(expected_dictionary, actual_dictionary)unittest将显示实际带有前缀的缺失键-,以及带有前缀的额外键+,就像执行a一样git diff old_branch new_branch

    是否直观,这是坚持该assertEqual(expected, actual)命令的唯一最令人信服的理由。如果碰巧不喜欢它,那么您最好还是接受它,因为“实用性胜过纯度”

  4. 最后,如果您需要一种帮助您记住顺序的方法,则此答案assertEqual(expected_result, actual_calculation)赋值语句order相比result = calculate(...)。这可能是记忆实际行为的好方法,但是恕我直言,这不是直觉上令人信服的理由。

所以,你去。快乐assertEqual(expect, actual)


1
对于第4点,有趣的是,分配的参数顺序实际上在计算的早期就引起了很多争论。一些CS专家认为,使用顺序顺序可以将其读取为“值存储在值中”,而whjich对数学等式的混淆则要少得多(并且使用不同于'='的东西进行赋值,例如某种箭头,例如'->')。
克里斯,

5

的一个别有用心的目的assertEqual()是为人类读者演示代码。因为最简单的函数调用是result = function(parameters),所以习惯了想左边返回值右边调用

因此,记录函数的测试将在左侧显示一个文字,在右侧显示一个调用。

assertEqual(15, sum((1,2,3,4,5)))

即,(预期的,实际的)。与表达式类似。

assertEqual(4, 2 + 2)

如果您喜欢排队(尽管PEP8),则期望的参数有助于在左侧变短:

assertEqual(42, 2 * 3 * 7)
assertEqual(42, (1 << 1) + (1 << 3) + (1 << 5))
assertEqual(42, int('110', int('110', 2)))

感谢Andrew WeimholtGanesh Parameswaran提出的这些公式。


2

xUnit测试约定是预期的/实际的。因此,对于许多人来说,这是自然的顺序,因为那是他们所学的。

有趣的是,与xUnit框架的约定有所不同,qunit适用于实际/预期。至少使用javascript,您可以创建一个新函数来封装旧函数并将其分配给原始变量:

var qunitEquals = equals;
equals = function(expected, actual, message) {
    qunitEquals(actual, expected, message);
};

-2

我听到的解释是它来自TDD。

在“测试驱动开发”中,从测试开始,然后编写代码。

通过编写期望值开始断言,然后调用应该产生期望值的代码,是该思维定式的迷你版本。

当然,这可能只是人们讲的一个故事。不知道这是有意识的原因。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.