1. 使用assert声明
pytest与unittest对比:
pytest | unittest |
---|---|
assert something | assertTrue(something) |
assert a == b | assertEqual(a,b) |
assert a <= b | assertLessEqual(a,b) |
pytest有一个重要的功能,它可以重写assert关键字。pytest会截断对原生assert的调用,替换为pytest定义的assert,从而提供更多的失败信息和细节。
增加一个运行失败的测试用例:
def test_failure():
"""使用assert"""
t1 = 'a'
t2 = 'b'
assert t1 == t2
运行结果如下:
失败的测试用例在行首都用一个>号来表示。以E开头的行是pytest提供的额外判定信息,用于帮助我们了解异常的具体情况。
2. 预期异常
定义一个Task类型数据,并增加一个会引起TypeError异常的测试用例:
Task = namedtuple('Task', ['summary', 'owner', 'dine', 'id'])
Task.__new__.__delattr__ = (None, None, False, None)
def test_task():
task = Task();
运行结果如下:
在原本基础上,增加with pytest.raises(TypeError):
def test_task():
with pytest.raises(TypeError):
task = Task();
再次运行,运行结果如下:
测试用例test_task()中有with pytest.raises(TypeError)声明,意味着无论with中的内容是什么,都至少会发生TypeError异常。如果测试通过,说明确实发生来我们预期的TypeError异常;如果测试失败,说明抛出的是与我们所预期不一致的异常。
另外,可以通过as excinfo语句来获取异常消息的值。
with pytest.raises(TypeError) as excinfo:
3. 测试函数的标记
pytest提供了标记机制,允许你使用marker对测试函数做标记。一个测试函数可以有多个marker,一个marker也可以用勒标记多个测试函数。
例如,为了吧选定的测试用例加入冒烟测试,可以对它们添加统一的标识,如:@pytest.mark.smoke。
运行时,通过参数-m来指定:
pytest -m smoke
ps:-m具体用法可查看:《pytest测试实战》笔记:一
另外,带有相同标记的测试用例即使存放在不同文件下,也会被一起执行。
4. 跳过测试
pytest自身内置了一些标记:skip、skipif、xfail。
skip、skipif允许你跳过不希望运行的测试。
要跳过某个测试用例,只需简单地在测试函数上添加@pytest.mark.skip()装饰器即可。
实际上,可以给到跳过的测试用例添加理由和条件,比如希望它只在包版本低于0.2.0时才生效,应用skipif替代skip:
@pytest.mark.skipif(tasks.__version__ < 0.2.0,
reason = 'not supported until version 0.2.0')
skipif()的判断条件可以是任何Python表达式。
运行时可以通过-rs查看跳过的原因:
5. 标记预期会失败的测试
使用skip、skipif标记,测试用例会被直接跳过,不会被执行。使用xfail标记,告诉pytest执行此测试用例,但预期会失败。
添加两个测试用例,均使用了xfail来标记,区别在于demo1的assert为false,demo2的assert为true:
@pytest.mark.xfail()
def test_demo1():
t1 = 'a'
t2 = 'b'
assert t1 == t2
@pytest.mark.xfail()
def test_demo2():
t1 = 'a'
t2 = 'b'
assert t1 != t2
运行结果如下:
x:XFAIL,expected to fail,预期失败,实际上也失败了
X:XPASS,expected to fail but passed,预期失败,但实际运行并没有失败
6. 运行测试子集
运行测试子集有很多方式,不但可以选择运行某个目录、文件、类中的测试,还可以选择运行某一个测试用例(可能在文件中,也可能在类中)。
- 单个目录:以目录作为pytest的参数即可。
- 单个测试文件/模块:运行单个文件里的全部测试,以路径名加文件名作为pytest参数即可。
- 单个测试函数:运行单个测试函数,在文件名后面添加::符号和函数名。
- 单个测试类:运行测试类,在文件名后面添加::符号和类名(与运行单个测试函数类似)。
- 单个测试类中的测试方法:在文件名后面添加::符号和方法名。
7. 参数化测试
可以使用@pytest.mark.parametrize(argnames, argvalues)装饰器达到批量传送参数的目的。
testdata_add = [(1, 2, 3), (3.12, 4.12, 7.24), (-100, 200, 100), ("a", "b", "ab"), ("中文1", "中文2", "中文1中文2"), ("space", " ", "space "), ("null", "", "null")]
# 执行时会根据数据的数量执行对应条数的用例
@pytest.mark.parametrize("num1, num2, expect", testdata_add)
def test_add(num1, num2, expect): # 接收的变量名要和parametrize的一致
"""测试加法运算函数"""
result = add(num1, num2)
assert result == expect
运行结果如下:
也可以为测试类添加parametrize()转装饰器,这种情况下,该数据集会被传递给该类的所有类方法。
接下来,会在《pytest测试实战》笔记:三 中介绍如何使用fixture。fixture的作用是将一些非核心测试逻辑,如:测试数据的检索和生成,从测试函数中分离出来,以便于其他测试函数复用,同时保持这边边缘逻辑的一致性。