【问题标题】:Why not use python's assert statement in tests, these days?这些天,为什么不在测试中使用 python 的 assert 语句呢?
【发布时间】:2015-11-03 05:28:56
【问题描述】:

在 Python 测试中,为什么要使用断言方法:

self.assertEqual(response.status_code, 200)
self.assertIn('key', my_dict)
self.assertIsNotNone(thing)

与直接断言相反:

assert response.status_code == 200
assert 'key' in my_dict
assert thing is not None

根据docs

使用这些方法代替 assert 语句,以便测试运行器可以累积所有测试结果并生成报告

尽管这似乎是假的,但测试运行者可以累积结果并生成报告。在 related post 中,unutbu 表明 unittest 将引发 AssertionError,就像 assert 语句一样,那是 7 年前的事了,所以它也不是一个闪亮的新功能。

使用 pytest 等现代测试运行程序,断言辅助方法生成的失败消息不再具有可读性(可以说 unittest 的 camelCase 样式是 less 可读的)。 那么,为什么不在您的测试中使用 assert 语句? 有哪些明显的缺点?为什么 CPython 等重要项目还没有从 unittest 中移除?

【问题讨论】:

  • 我在unittest 之外工作,所以我使用print() 而不是assert。我认为这完全取决于您项目的复杂性和规模。
  • 这似乎主要是见仁见智。如果您认为assert 更好,那么没有什么可以阻止(除了您的同事不同意)您使用它。但是应该知道,您可能会覆盖 TestCase 类中的方法并自定义行为 - assert 语句没有这种灵活性。此外,TestCase.failureException 可能会被更改,这会使您认为它与assert 语句引发的异常相同的观点无效(如果您想将assert 视为错误而不是失败,则可以使用此方法)。
  • 简单的assert 更具可读性。因此,如果您的测试失败,请始终使用pytest
  • @o11c 是的,我同意。但问题更多的是,为什么所有这些self.assertStuff 方法都存在,为什么许多受人尊敬的库仍然积极使用它们。

标签: python unit-testing pytest assert nose


【解决方案1】:

使用assert 关键字或专用方法的主要区别在于输出报告。请注意,assert 后面的语句始终为TrueFalse,并且不能包含任何额外信息。

assert 3 == 4

只会在报告中显示AssertionError。 然而,

self.assertTrue(3 == 4)

提供一些额外信息:AssertionError: False is not true。不是很有帮助,但请考虑:

self.assertEqual(3, 4)

它告诉你AssertionError: 3 != 4 会更好。您阅读了报告,就知道它是什么类型的断言(平等测试)和所涉及的价值。

假设你有一些函数,并且想要断言它返回的值。 您可以通过两种方式做到这一点:

# assert statement
assert your_function_to_test() == expected_result

# unittest style
self.assertEqual(your_function_to_test(), expected_result)

如果出现错误,第一个除了断言错误之外不提供任何信息,第二个告诉你断言的类型(相等测试)和涉及的值(返回值和预期值)。

对于小型项目,我从不使用 unittest 样式,因为它的输入时间较长,但在大型项目中,您可能想了解更多有关错误的信息。

【讨论】:

  • 您可以将任意字符串添加到断言中,以包含在引发的AssertionError:assert 3 == 4, "3 != 4"中。
  • 更重要的是,测试运行器/测试框架能够自动生成上下文。
【解决方案2】:

我不完全确定我理解了这个问题。标题是“为什么现在不在测试中使用pythons assert 语句”

正如您所指出的,实际上 如果您使用像 pytest 这样的测试框架,则可以使用简单的断言然而 pytest 做了一些非常特别的事情来让它工作。它在运行测试之前重写测试代码中的普通断言。

参见https://docs.pytest.org/en/stable/writing_plugins.html#assertion-rewriting 声明:

pytest 的主要功能之一是使用简单的断言语句以及在断言失败时对表达式的详细自省。这是由“断言重写”提供的,它会在解析的 AST 被编译为字节码之前对其进行修改。

unittest 框架没有实现这种额外的复杂性。 (而且它额外的复杂性。Pytest 只重写测试用例中的断言,它不会重写测试代码使用的其他 python 库中的断言。所以你有时会发现pytest 在您的测试代码中遇到一个断言错误,但是没有关于断言失败的原因的详细信息,因为它没有重写您的代码。因此,您只会得到一个普通的AssertionError,而没有任何信息失败的原因。)

相反,unittest 提供了assertEqual 之类的方法,因此它可以:

  1. 知道这是一个失败的测试断言,而不是其他一些未处理/意外的异常;和
  2. 它可以提供关于为什么不满足断言的信息。 (python 中的简单断言只会引发 AssertionError。它没有说,例如 AssertionError because 1 != 2

Pytest 通过在运行测试代码之前重写抽象语法树来完成第 1 和第 2 项。 Unittest 采用更传统的方法,要求开发人员使用特定的方法。

所以基本上答案是:这是测试框架之间的实现差异。换句话说,Python 的内置 assert 语句没有提供有关失败原因的调试信息。因此,如果您想了解更多信息,您需要决定如何实施。

Unittest 比 pytest 简单得多。 Pytest 很棒,但它也复杂得多。

【讨论】:

  • 最好将额外的复杂性放在测试框架本身中,而不是出现在每个测试套件中。这个问题最初的目的更像是“为什么 unittest 不做一些事情来自省断言失败,而不是要求开发人员使用所有这些丑陋的辅助方法”,但事后看来,答案很明显只是“因为他们没有那样实现它”。
【解决方案3】:

我认为对于当前的 pytest 版本,您可以在大多数情况下使用 assert,因为上下文是由测试框架 (pytest 2.9.2) 重构的:

    def test_requests(self):
        ...
>       self.assertEqual(cm.exception.response.status_code, 200)
E       AssertionError: 404 != 200

看起来很像

    def test_requests(self):
        ...
>       assert cm.exception.response.status_code == 200
E       AssertionError: assert 404 == 200
E         -404
E         +200

理论上,使用self.assertXxx() 方法将允许 pytest 计算没有失败的断言数量,但 AFAIK 没有这样的指标。

【讨论】:

    【解决方案4】:

    您找到的文档的链接是正确答案。如果您不喜欢这种编写测试的方式,我强烈建议您使用 pytest:

    http://pytest.org/latest/

    pytest 做了很多工作,让你可以按照自己的方式使用 assert 语句。它还具有许多其他非常好的功能,例如它们的固定装置。

    【讨论】:

    • 我确实使用 pytest,如问题中所述。当测试运行者愉快地累积所有测试结果并生成报告时,无论您使用断言语句还是辅助函数,文档中的声明如何被认为是正确的?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-13
    • 2018-09-26
    • 1970-01-01
    • 2016-01-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多