《pytest测试实战》读书笔记

第一章 pytest入门

常用启动命令

1.pytest会在当前目录及子目录寻找测试用例
2.Task结构,用于UI层和API层之间传递信息:

from collections import namedtuple
Task = namedtuple('Task',['summary','owner','done','id'])
# __new__.__defaults__创建默认Task对象,不必指定所有属性
Task.__new__.__defaults__ = (None,None,False,None) 
def test_defaluts():
    t1=Task()  # 默认值
    t2=Task(None,None,False,None)
    assert T1 == T2
# 演示使用属性名(而不是索引)来访问对象成员
def test_member_access():
    t=Task('buy milke','brain') 
    assert t.summary == 'buy milke'
    assert t.owner == 'brain'
    assert (t.done,t.id) == (False,None)
# `_asdict` 和 `_replace`函数

3.运行单个测试用例: pytest -v test.py::test_asdict
4.常用命令行
--collect-only: 只是展示给定配置下运行哪些测试用例,方便检查
-k: 允许使用表达式指定运行测试方法

pytest -k "asdict or defaluts" --collect-only  
pytest -k "asdict or defaluts" 
pytest -v -k "asdict or defaluts"  

-m 运行指定标记的用例
-m "mark1 and mark2"同时标记mark1和mark2的
-m "mark1 or mark2"标记mark1或mark2的
-m "mark1 and not mark2"标记mark1,过滤掉mark2的
-x 遇到失败停止pytest运行
--maxfail=num 指定失败几次,停止运行 。num=1时同-x (指运行所有指定测试用例时,失败N次后停止)
-s 与 --capture=method 打印符合标准的输出或信息,如print
-l /--showlocals 测试失败时打印出局部变量名和值,可避免一些不必要的print
-v 或者 --verbose : 可以查看具体运行详情
-q/--quiet:与-v相反,简化输出信息,常和--tb=line(仅打印异常的代码位置)搭配使用
--tb=style决定失败时输出信息的显示方式 (short,line,no:关闭,lang等)

第二章 编写测试函数

演示如何使用assert,如何处理可预期和不可预期的异常,如果借助类,模块,目录来组织测试,以便管理大量的测试。使用mark为预期会失败的测试做标记

测试示例程序

  1. test/func/init.py和test/unit/init.py都是空文件,作用是给pytest提供搜索路径,找到测试根目录和pytest.ini文件。(conftest.py也可以统一放在test根目录)
  2. pytest.ini是可选的,它保存了pytest在该项目下的特定配置。
  3. conftest.py是pytest的“本地插件库”,包含hook函数和fixture。hook可以将自定义逻辑引入pytest,用于改善pytest的执行流程,fixture则是用于测试前后执行配置及销毁逻辑的外壳函数,可以传递测试中用到的资源。
  4. 测试demo下载 传送门
    安装 cd code/tasks_proj pip install .

使用assert

  1. pytest.org上有很多复杂的assert例子
  2. 预期异常
with pytest.raises(TypeError):
    task.add(task='not a task object')
# 无论with内容是什么,都会发生TypeError异常。如果测试通过,说明发生了预期的TypeError异常;如果抛出其他类型的异常,则与预期不一致,测试失败。

with pytest.raises(ValueError) as excinfo:
    tasks.start_task_db('some','mysql')
exception_msg=excinfo.value.args[0]
assert exception_msg == "db_type must be a 'tiny' or 'mongo'"
# 不止校验异常类型,还可以校验异常消息

标记测试

  1. @pytest.mark.skip()装饰器 : 跳过测试 (skipif可设置跳过条件)
  2. @pytest.mark.xfail():告诉pytest运行此测试,但我们预期会失败(如果实际失败,则记过为xfail,如果实际成功,则结果为XPASS,可在pytest.ini中强制执行结果为fail
[pytest]
xfail_strict=ture

参数化

1.@pytest.mark.parametrize()第一个参数为逗号分隔的字符串列表,第二个参数是值列表parametrize()
2.parametrize()在测试运行会优化测试用例可视化,也可引入额外自定义参数。ids:标识用例的id

@pytest.mark.parametrize('a,b', [['2', '3'], ['1', '4']], ids=['2和3', '1和4'])
def test_param(a, b):
    print("===", int(a))
    assert int(a) + int(b) == 5

3.parametrize()可以加在测试类上,这样数据集会被传递给所有类方法
4.parametrize()可以在参数值旁定义id做标识,pytest.param(<value>,id='something') 在id不能被参数化批量生成时,需要自定义时,很管用

param = [
    pytest.param(1, 2, 5, id='failed'),
    pytest.param(3, 4, 7, id='success')]

@pytest.mark.parametrize('a,b,expect', param)
def test_param2(a, b, expect):
    assert int(a) + int(b) == int(expect)

第三章 pytest fixture

安装tasks,cd code;pip install ./tasks_proj/
1.fixture函数会在测试函数之前运行,如果fixture函数包含yield,那么系统会在yield处停止,转而运行测试函数,等测试函数运行完再回到fixture,运行yield后的代码
2.--setup-show 回溯fixture的执行过程
3.fixture内发生的assert异常结果为ERROR,测试函数assert发生的异常结果为FAIL
4.usefixtures
5.autouse=True 作用域内的函数都运行该fixture

fixture作用范围scope

functionfixture默认值,每个测试函数运行一次,配置代码再测试用例运行之前运行,销毁代码再测试用例运行之后运行
class每个测试类运行一次,无论测试类里有多少类方法都可以共享这个fixture
module每个模块只需要运行一次,无论模块里有多少个测试函数,类方法或其他fixture都可以共享这个fixture
session每次会话只需要运行一次,一次pytest会话中的所有测试函数,方法都可以共享这个fixture
--setup-show 可以观察每个fixture被调用的次数,以及在各自作用范围下执行配置,销毁逻辑的顺序
fixture只能使用同级别的或者更高级别的fixture,不能反过来

fixture参数化

@pytest.fixture(params=tasks,ids=task_ids) #也可参数化,也可指定id
def a_task(request):  # request是pytest内建的fixture之一,代表fixture的调用状态,有一个param字段,会被params列表的一个元素填充
    return request.param

2.fixture_value 内置fixture?

  1. 对测试函数进行参数化处理,可以多次运行的只是该测试函数;而使用参数化fixture,每个使用该fixture的测试函数都可以被运行多次,这一特性非常强大。

第四章 内置fixture

tmpdir和tmpdir_factory

内置的tmpdirtmpdir_factory负责在测试开始运行前创建临时文件或目录,并在测试结束后删除。单个测试使用tmpdir,多个测试使用tmpdir_factory
tmpdir的作用范围是函数级别,tmpdir_factory的是会话级别 ,如果需要其他级别的,需要创建一个新的fixture。

def test_tmpdir(tmpdir):
    a_file=tmpdir.join("a.txt")
    b_dir=tmpdir.mkdir("something")
    b_file=b_dir.join("b.txt") 
    a_file.write("aaa")
    b_file.write("bbb")
    assert a_file.read()=="aaa"  and b_file.read()=="bbb"

pytestconfig

内置的pytestconfig可以通过命令行参数,选项,配置文件,插件,运行目录等方式来控制pytest。它是request.config的快捷方式,被称为“pytest配置对象”

def pytest_addoption(parser):  #pytest的hook函数pytest_addoption,可添加命令行选项
    parser.addoption("--myopt",action="store_true",help="some my option")
    parser.addoption("--foo",action="store",default="bar",help="foo:bar or baz")

def test_option(pytestconfig):
    print(pytestconfig.gettoption('myopt'))
    print(pytestconfig.gettoption('foo'))
    print(pytestconfig.option.foo)
# 一些例子
def test_pytestconfig(pytestconfig):
    print(pytestconfig.args)
    print(pytestconfig.inifile)
    print(pytestconfig.invocation_dir)
    print(pytestconfig.rootdir)
    print(pytestconfig.getoption('showlocals'))

使用cache

cache用于测试会话传递给下一段会话
--last-failed(仅运行上次未通过的)和--failed-first (之前未通过的首先运行)很好的展示cache的功能,看看cache是如何存储这些标识数据的。
--cache-show可以显示cache存储的信息
--clear-cache 运行前清空cache缓存

def test_cache(cache):
    cache.get(key,default)
    cache.set(key,value)

使用capsys

允许使用代码读取stdout和stderr,capsys.redouterr() 也可以临时禁止抓取日志输出capsys.redouterr()
pytest通常会抓取输出,仅当全部用例结束后,抓取到的日志才会显示出来。--s参数可以关闭这个功能,在测试运行期就把输出直接发送到stdout,但有时可能只需要部分信息,则可以用 capsys.disabled()临时让输出绕过默认的输出捕获机制
capsys.redouterr()

def test_disabled(capsys):
    with.capsys.disabled():
        print("aaaa")
    print("bbb")  # pytest -q 和 pytest -q -s的区别

monkeypatch

在运行期间对类或模块进行动态修改。常用于替换被测试代码的部分运行环境,或将输入依赖或输出依赖替换成更容易测试的对象或函数。测试结束后,无论结果是通过还是失败,代码都会复原(所有修改都会撤销)
例如:修改环境变量monkey.setenv('HOME',tmpdir.mkdir('home'))
其他函数(setattr )

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容