“软件测试的主要工作目标是验证实际结果与预期结果是一致的,在自动化软件测试中,通过断言来实现这一目的。Pytest中断言是通过Python原生的assert语句实现的,对Python原生的assert语句进行了优化,当发生断言失败时,错误信息更加丰富,方便测试时快速定位问题原因。”
优秀的测试框架都提供了断言的方法,比如TestNG中的assertTrue、assertEquals、assertSame等等。前面给大家介绍过Pytest的实践《pytest接口测试轻松入门》,本文将详细介绍Pytest的断言,与TestNG相比它更加简单,只有一个assert语句,但是功能非常强大并且简单易用。
1、Python原生的assert
Python中assert语句通常用来对代码进行必要的检查,确定某种情况一定发生,或者一定不会发生。
Python 的 assert 语句的语法是这样的:
assert expression1 ["," expression2]
expression1往往是一个条件表达式,如果条件表达式为True,则什么也不做,相当于执行了 pass 语句;如果条件表达式为False,便会抛出异常 AssertionError,并返回具体的错误信息expression2。看一个实际例子:
# content of my_assertion.py def assertion(): assert [1, 2, 3] == [1, 2, 4], "left is [1,2,3], right is [1,2,4]" if __name__ == '__main__': assertion()
执行一下上面的代码看看结果:
$ python my_assertion.py Traceback (most recent call last): File "my_assertion.py", line 5, in <module> assertion() File "my_assertion.py", line 2, in assertion assert [1, 2, 3] == [1, 2, 4], "left is [1,2,3], right is [1,2,4]" AssertionError: left is [1,2,3], right is [1,2,4]
可见,assert后面的条件表达式为False,抛出了AssertionError,并显示了错误信息left is [1, 2, 3], right is [1, 2, 4]。
不过,这里还有一点小小的缺憾。并没有明确告诉开发人员,条件判断失败的具体位置。需要开发人员自己对比才发现,==左边的第三个元素和右边的第三个元素不一样。
2、Pytest的assert优点
软件测试工作,经常会遇到断言失败的情况。如果每次失败,都需要测试工程师人眼去观察失败的具体原因和出错的位置,那将是非常耗时的。强大的Pytest也考虑到了广大测试工程师面临的问题,因此对Python原生的assert语句进行了优化和改进,主要在是当断言失败时,将错误的具体信息和位置显示出来,让测试工程师对失败原因一目了然。
还是上面的例子,将其放入到测试用例(test_开头的函数)中:
# content of test_assertion.py def test_assertion(): assert [1, 2, 3] == [1, 2, 4], "left is [1,2,3], right is [1,2,4]"
执行测试用例后的信息输出如下:
def test_assertion(): > assert [1, 2, 3] == [1, 2, 4], "left is [1,2,3], right is [1,2,4]" E AssertionError: left is [1,2,3], right is [1,2,4] E assert [1, 2, 3] == [1, 2, 4] E At index 2 diff: 3 != 4 E Full diff: E - [1, 2, 4] E ? ^ E + [1, 2, 3] E ?
是不是有种很爽的感觉?pytest明确显示出了错误的位置是index为2的元素左右不相等。这一点点小小的改进大大提高了测试失败时定位出错原因的效率。
在测试用例中执行assert语句,才有上面的效果,这是因为Pytest对assert语句进行了重写。在非测试用例中的assert语句,比如测试项目中的一些utils函数中,使用assert还是Python原生的效果。
3、Pytest 断言的用法
在自动化测试用例中,最常用的断言是相等断言,就是断言预期结果和实际结果是一致的。通常我们断言的预期结果和实际结果的数据类型是字符串、元组、字典、列表和对象。Pytest通过assert和==能够完美支持对这些数据类型的相等断言。下面来介绍几种常见的数据类型的断言操作。
3.1 断言字符串
断言字符串非常简单,只需要将预期和实际的字符串,分别写在==两边,当发生断言失败时,将会列出第一个不相等元素的下标。下面是几个在实际测试工作中经常用到的几种字符串断言方式。
# content of test_assertions.py class TestAssertions(object): def test_string_1(self): assert "spam" == "eggs" def test_string_2(self): assert "foo 1 bar" == "foo 2 bar" def test_string_3(self): assert "foo\nspam\nbar" == "foo\neggs\nbar" def test_string_4(self): def f(): return "streaming" assert f().startswith('S')
执行一下这些测试用例,看下输出效果,核心部分如下:
============================================================ FAILURES ============================================================ __________________________________________________ TestAssertions.test_string_1 __________________________________________________ self = <test_assertions.TestAssertions object at 0x10911a4d0> def test_string_1(self): > assert "spam" == "eggs" E AssertionError: assert 'spam' == 'eggs' E - eggs E + spam tests/test_assertions.py:3: AssertionError __________________________________________________ TestAssertions.test_string_2 __________________________________________________ self = <test_assertions.TestAssertions object at 0x10911a890> def test_string_2(self): > assert "foo 1 bar" == "foo 2 bar" E AssertionError: assert 'foo 1 bar' == 'foo 2 bar' E - foo 2 bar E ? ^ E + foo 1 bar E ? ^ tests/test_assertions.py:6: AssertionError __________________________________________________ TestAssertions.test_string_3 __________________________________________________ self = <test_assertions.TestAssertions object at 0x10911c2d0> def test_string_3(self): > assert "foo\nspam\nbar" == "foo\neggs\nbar" E AssertionError: assert 'foo\nspam\nbar' == 'foo\neggs\nbar' E foo E - eggs E + spam E bar tests/test_assertions.py:9: AssertionError __________________________________________________ TestAssertions.test_string_4 __________________________________________________ self = <test_assertions.TestAssertions object at 0x109106a90> def test_string_4(self): def f(): return "streaming" > assert f().startswith('S') E AssertionError: assert False E + where False = <built-in method startswith of str object at 0x1090f7bb0>('S') E + where <built-in method startswith of str object at 0x1090f7bb0> = 'streaming'.startswith E + where 'streaming' = <function TestAssertions.test_string_4.<locals>.f at 0x10914b440>() tests/test_assertions.py:15: AssertionError
再次感觉到测试结果一目了然。
3.2 断言函数或者接口返回值
对函数返回值、接口返回值的断言,应该是软件自动化测试中最常见的场景了。这里以函数返回值的断言为例:
def test_function(): def f(): return [1, 2, 3] assert f() == [1, 2, 4]
执行这个测试用例,看下输出的错误信息:
============================================================ FAILURES ============================================================ _________________________________________________________ test_function __________________________________________________________ def test_function(): def f(): return [1, 2, 3] > assert f() == [1, 2, 4] E assert [1, 2, 3] == [1, 2, 4] E At index 2 diff: 3 != 4 E Full diff: E - [1, 2, 4] E ? ^ E + [1, 2, 3] E ? ^ tests/test_assertions.py:22: AssertionError
可以看到,输出信息中包含了函数的返回值,并且显示了返回值与预期结果不一致的元素是index为2的元素。