《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 )

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容