pytest使用

个人笔记 并没有整理 仅供参考

 

1 安装  pip3 install  pytest

 

2 cmd 窗口 运行  pytest 或  py.test

-q, --quiet decrease verbosity( 显示简单结果)

Pytest -h  说明文档

 

 

1 测试文件以test_开头  或结尾

2 测试类以Test开头  并且不能带有 __init__ 方法

3 测试函数以test_开头

4 断言使用assert

 

 

 

执行用例规则

 

1 执行某个目录下所有的用例

Pytest 文件名

2 执行某个py文件下用例

Pytest 脚本名称.py

3 -k 按关键字匹配

Pytest -k myclass

 

4 按照节点运行

1 pytest test_mode.py::test_func 运行某个函数

2 pytest test_mode.py::TestClass::test_method 运行某个测试类中的某个方法

 

5 标记表达式;

Pytest -m slow 将运行所有被@pytest.mark.slow装饰器修饰的所有测试

6 从包里面运行

Pytest --pyargs pkg.testing

这将导致pkg.testing 并使用其文件系统位置来查找和运行测试用例

 

7 -x  遇到错误的时候就停止测试

   Pytest -x test_class.py

 

8 --maxfail=num   当错误到达多少个的时候就停止用例的运行

Pytest --maxfail =4

9  unittest 编写的脚本也可以 使用pytest执行  需要改该工程设置默认的运行器:file->Setting->Tools->Python Integrated Tools->项目名称->Default test runner->选择py.test

10 pycharm    pytest.main(‘q’,’pytest2.py’)  main函数中 需要传入多个参数 需用 ,隔开

 

 

用例运行级别:

模块级

setup_module/teardown_module

开始于模块始末,全局的

函数级

setup_function/teardown_function

只对函数用例生效(不在类中)

类级

setup_class/teardown_class

只在类中前后运行一次(在类中)

方法级

setup_method/teardown_method

开始于方法始末(在类中)

类里面的

setup/teardown

运行在调用方法的前后

 

1 setup_function/teardown_function  每个用例开始和结束就调用一次

2 setup_module是所有用例开始前只执行一次,teardown_module是所有用例结束后只执行一次.

3   1.setup/teardownunittest里面的setup/teardown是一样的功能,setup_classteardown_class等价于unittest里面的setupClassteardownClass   这在用例执行前的初始化 至关重要  比如实例化 数据库

 

 

import pytest # 类和方法 

 class TestCase(): 

def setup(self):

  print("setup: 每个用例开始前执行")

def teardown(self): 

print("teardown: 每个用例结束后执行")

def setup_class(self):

 print("setup_class:所有用例执行之前")

def teardown_class(self):

 print("teardown_class:所有用例执行之前")

 def setup_method(self):

 print("setup_method: 每个用例开始前执行")

 def teardown_method(self):

 print("teardown_method: 每个用例结束后执行")

 def test_one(self):

 print("正在执行----test_one")

 x = "this" assert 'h' in x

def test_two(self):

 print("正在执行----test_two")

x = "hello"

 assert hasattr(x, 'check')

 def test_three(self):

 print("正在执行----test_three")

 a = "hello" 

b = "hello world"

 assert a in b

if __name__ == "__main__":

pytest.main(["-s", "test_fixtclass.py"])

 

4 运行级别

setup_module/teardown_module的优先级是最大的,然后函数里面用到的setup_function/teardown_function与类里面的setup_class/teardown_class互不干涉

 

 

 

 

 

 

 

 

 

Fixture   相比setupteardown  应该来说有一下几点优势

命名方式灵活  不局限setupteardown  这个命名

Conftest.py 配置里可以实现数据共享,不需要import就能自动找到一些配置

Scope=”module” 可以实现多个.py 跨文件共享前置, 每一个.py文件调用一次

Scope = ‘session’   可以实现多个.py 跨文件使用一个session来完成多个用例

 

 

Fixture 参数的解释:

fixture(scope="function", params=None, autouse=False, ids=None, name=None): """使用装饰器标记fixture的功能  可以使用此装饰器(带或不带参数)来定义fixture功能。 fixture功能的名称可以在以后使用      引用它会在运行测试之前调用它:test模块或类可以使用pytest.mark.usefixturesfixturename标记。 测试功能可以直接使用fixture名称作为输入参数,在这种情况下,夹具实例从fixture返回功能将被注入。 :arg scope: scope 有四个级别参数 "function" (默认), "class", "module" or "session". :arg params: 一个可选的参数列表,它将导致多个参数调用fixture功能和所有测试使用它 :arg autouse: 如果为True,则为所有测试激活fixture func 可以看到它。 如果为False(默认值)则显式需要参考来激活fixture :arg ids: 每个字符串id的列表,每个字符串对应于params 这样他们就是测试ID的一部分。 如果没有提供ID它们将从params自动生成 :arg name: fixture的名称。 这默认为装饰函数的名称。 如果fixture在定义它的同一模块中使用,夹具的功能名称将被请求夹具的功能arg遮蔽; 解决这个问题的一种方法是将装饰函数命名 “fixture_ <fixturename>”然后使用”@ pytest.fixturename ='<fixturename>'“”

+-

Fixtures可以选择使用yield语句为测试函数提供它们的值,而不是return 在这种情况下,yield语句之后的代码块作为拆卸代码执行,而不管测试结果如何。fixture功能必须只产生一次    假如中间又个别用例执行失败  那么 yield 也会 等待所有的测试用例执行完毕 才会执行 结束操作

 

 

 

Fixture 中的conftest.py   配置  

该文件名称固定 作用域同级别的package下 并且   __init__ 文件

 

 

测试报告的设定

需要安装pytest-html  pytest 的插件

Pytest  --html=reportname.html

默认生成在  当前运行脚本的同级目录 ,一般来说测试报告要和脚本分离,可以指定路径

Pytest  --html=./reportdirname/reportname.html   但是如果指定了多个运行py文件那么 这些报告是独立的

  如果想要合并

Pytest --html=report.html --self-contained-html

 

 

默认情况下,结果表中的所有行都将被展开,但具测试通过的行除外Passed

可以使用查询参数自定义此行为:?collapsed=Passed,XFailed,Skipped

 失败用例可以自动截图    需要改写一些东西。

原理: 根据用例的执行状态  自动截图  ,然后保存为二进制文件  然后存在本地,然后 利用js 动态的在已经生成的报告用动态插入 截图

 

 

 

失败重新跑:

1 pip install  pytest-rerunfailures   

2 pytest --reruns 2 --html=report.html

 

 

参数化 pytest.mark.parametrize装饰器可以实现测试用例参数化。

 

import pytest

 @pytest.mark.parametrize("test_input,expected", [ ("3+5", 8), ("2+4", 6), ("6 * 9", 42), ])

 def test_eval(test_input, expected):

 assert eval(test_input) == expected

 if __name__ == "__main__":

pytest.main(["-s", "test_canshu1.py"])

 

 

 

 

也可以标记单个测试用例参数化  比如用mark.xfail  标记某个测试用例 错误

import pytest

 @pytest.mark.parametrize("test_input,expected", [ ("3+5", 8), ("2+4", 6), pytest.param("6 * 9", 42, marks=pytest.mark.xfail), ])

 def test_eval(test_input, expected):

 print("-------开始用例------")

assert eval(test_input) == expected

if __name__ == "__main__":

pytest.main(["-s", "test_canshu1.py"])

 

组合参数化:

按照关键字传参  不关注 装饰器 执行顺序

 

import pytest

@pytest.mark.parametrize("x", [0, 1])

 @pytest.mark.parametrize("y", [2, 3])

 def test_foo(x, y):

 print("测试数据组合:x->%s, y->%s" % (x, y))

 if __name__ == "__main__":

pytest.main(["-s", "test_data.py"])

 

 

命令行传参 

  需要在conftest 文件中  定义一个被fixture 的函数  然后在命令行中带上就行

  pytest -s test_case1.py --cmdopt type2

 

 

 

断言:

Pytest  允许使用标准的python内置 assert  

正常断言:

 

异常信息:

Assert a%2==0,”异常信息str”

异常断言:如果需要对返回的异常进行再次断言  需要根据 异常的 typevalue

例如1/0 异常类型  ZeroDivisionErrordivision by zero

import pytest

def test_zero_division():

 '''断言异常'''

 with pytest.raises(ZeroDivisionError) as excinfo:

 1 / 0 # 断言异常类型type

 assert excinfo.type == ZeroDivisionError

# 断言异常value 

assert "division by zero" in str(excinfo.value)

 

 

正常断言:

Assert xx

判断xx为真

Assert not xx

判断xx为假

Assert a in b

判断b是否包含a

Assert a==b

A是否等于b

Assert a!=b

A是否不等于b

 

 

  

 

Skip  pytest.mark.skip  可以标记无法在某些平台上无法运行的测试功能,或者希望失败的测试功能

skip意味着只有在满足某些条件时才希望测试通过,否则pytest应该跳过运行测试。 常见示例是在非Windows平台上跳过仅限Windows的测试,或跳过测试依赖于当前不可用的外部资源(例如数据库)。

xfail意味着您希望测试由于某种原因而失败。 一个常见的例子是对功能的测试尚未实施,或尚未修复的错误。 当测试通过时尽管预计会失败(标有pytest.mark.xfail),它是一个xpass,将在测试摘要中报告。

pytest计数并分别列出skipxfail测试。 未显示有关跳过/ xfailed测试的详细信息默认情况下,以避免混乱输出。 您可以使用-r选项查看与“short”字母对应的详细信息显示在测试进度中

 

 

1 skip 直接标记 也可以传递一个可选的的原因 reason 并没有特别的意义

  只是 注释 跳过的原因

@pytest.mark.skip(reason="no way of currently testing this") def test_the_unknown():

 

 

2 skipif 当条件为真的时候 跳过

import sys

 @pytest.mark.skipif(sys.version_info < (3,6), reason="requires python3.6 or higher")

 def test_function():

 

可以在在模块之间  共享 skipif  

  import mymodule

minversion = pytest.mark.skipif(mymodule.__versioninfo__ < (1,1), reason="at least mymodule-1.1 required")

 @minversion def test_function(): ...

 

 

也饿可以导入标记 在另外模块中重复使用它

from test_mymodule import minversion

@minversion 

def test_anotherfunction(): ...

 

 

 

Skip 类或模块

 同上

 

 

Skip 缺少导入依赖项

 

您可以在模块级别或测试或测试设置功能中使用以下帮助程序

docutils = pytest.importorskip("docutils")

如果无法在此处导入docutils,则会导致测试跳过结果。 你也可以跳过库的版本号

docutils = pytest.importorskip("docutils", minversion="0.3")

将从指定模块的__version__属性中读取版本。

 

 

 

 

 

 

 

函数传参和fixture传参数request

 

如果我们需要重复执行一个函数,那么就绪要动态传染  比如登录 之后 才能进行其他操作

1  @pytest.mark.parametrize

2 request 参数  给fixture传参 固定的参数名称

test_user_data = ["admin1", "admin2"]

 @pytest.fixture(scope="module")

 def login(request): 

     ‘’’

     Request 固定写法 用于给login 传染数

’’’

user = request.param

print("登录账户:%s"%user)

return user

 

添加indirect=True参数是为了把login当成一个函数去执行,而不是一个参数

 

@pytest.mark.parametrize("login", test_user_data, indirect=True)

 def test_login(login):

 '''登录用例''' 

a = login print("测试用例中login的返回值:%s" % a)

  assert a != ""

 if __name__ == "__main__":

pytest.main(["-s", "test_02.py"])

 

 

如果需要request 传入多个参数  ,只需要 将列表中的元素 嵌套就行

 test_user_data = [{"user": "admin1", "psw": "111111"}, {"user": "admin1", "psw": ""}]

 

 

 

 

Mark 标记

Pytest 中可以支持自定义标记 ,自定义标记可以把一个web项目划分多个模块, 然后指定模块名称执行。一个大项目自动化用的时候  也可以划分 多个模块

 

import pytest

 @pytest.mark.webtest 

def test_send_http():

  pass # perform some webtest test for your app

 def test_something_quick(): 

Pass

 def test_another(): 

pass 

class TestClass:

  def test_method(self):

 pass 

if __name__ == "__main__":

pytest.main(["-s", "test_server.py", "-m=webtest"])

不想运行就用 not

pytest.main(["-s", "test_server.py", "-m=’not webtest’"])

指定函数节点 id

pytest.main(["-v", "test_server.py::TestClass::test_method"])

运行多个节点

pytest.main(["-v", "test_server.py::TestClass", "test_server.py::test_send_http"])

 

匹配用例名称

 pytest -v -k http

 

 

 

用例之间的关联  如果a用例失败,跳过测试用例bc 并标注为xfail

当用例a失败的时候,如果用例b和用例c都是依赖于第一个用例的结果,那可以直接跳过用例bc的测试,直接给他标记失败xfail
用到的场景,登录是第一个用例,登录之后的操作b是第二个用例,登录之后操作c是第三个用例,很明显三个用例都会走到登录。
如果登录都失败了,那后面2个用例就没测试必要了,直接跳过,并且标记为失败用例,这样可以节省用例时间。

设计用例:

  • 把登录写为前置操作
  • 对登录的账户和密码参数化,参数用canshu = [{"user":"amdin", "psw":"111"}]表示
  • 多个用例放到一个Test_xxclass
  • test_01test_02test_03全部调用fixture里面的login功能
  • test_01测试登录用例
  • test_02test_03执行前用if判断登录的结果,登录失败就执行,pytest.xfail("登录不成功, 标记为xfail")

canshu = [{"user":"amdin", "psw":"111"}]

 @pytest.fixture(scope="module") 

def login(request):

 user = request.param["user"]

 psw = request.param["psw"]

 print("正在操作登录,账号:%s, 密码:%s" % (user, psw))

 if psw:

 return True

 else:

return False

 @pytest.mark.parametrize("login", canshu, indirect=True)

 class Test_xx():

 def test_01(self, login):

 '''用例1登录''' 

result = login print("用例1%s" % result)

assert result == True 

def test_02(self, login): 

result = login

print("用例3,登录结果:%s" % result)

if not result:

pytest.xfail("登录不成功, 标记为xfail")

 assert 1 == 1 

def test_03(self, login): 

result = login

print("用例3,登录结果:%s" %result)

 if not result:

pytest.xfail("登录不成功, 标记为xfail")

assert 1 == 1 

if __name__ == "__main__":

 pytest.main(["-s", "test_05.py"])

 

 

autouse设置为True,自动调用fixture功能

 

以上介绍过

 

 

配置:

Pytest 配置文件可以改变pytest的运行方式,他是一个固定的pytest.ini 文件,读取配置信息,按照指定的方式去运行。

 

1 Pytest.ini 中 的 addopts=-rsxX  标识pytest  报告所有测试用例被跳过,预计失败,预计失败但是实际被通过的原因

2 mark 标记  @pytest.mark.标记名称

有时候标签多了,不容易记住,为了方便后续执行指令的时候能准确使用mark的标签,可以写入到pytest.ini文件

[pytest] markers = webtest: Run the webtest case hello: Run the hello case

标记好之后,可以使用pytest --markers查看到

 

3 禁用xpass

设置xfail_strict = true可以让那些标记为@pytest.mark.xfail但实际通过的测试用例被报告为失败

4  addopts  

ddopts参数可以更改默认命令行选项,这个当我们在cmd输入指令去执行用例的时候,会用到,比如我想测试完生成报告,指令比较长

pytest -v --rerun 1 --html=report.html --self-contained-html

[pytest] markers = webtest: Run the webtest case hello: Run the hello case

 xfail_strict = true

addopts = -v --rerun 1 --html=report.html --self-contained-html

 

相当于定义了一个变量  执行这个变量就性了

 

 

 

 

Pytestdoctest测试框架   是python自带的一个模块 是单元测试的一种。Doctest模块回搜索那些看起来像交互式会话的python代码片段  在python中 有>>>标识的 就认为是 cmd交互环境

 

 

Fixture

Fixture 作为参数传入 返回值可有可无 并且可以返回可变数据类型  errfailed的区别

Err一般是程序执行错  但是在fixture  断言错误就回报错,因为不是test开头

Fixturefixture之间可以相互调用

 

import pytest

@pytest.fixture() 

def first(): 

print("获取用户名")

a = "yoyo"

 return a

@pytest.fixture()

 def sencond(first):

 '''psw调用user fixture'''

 a = first

 b = "123456" 

return (a, b)

 def test_1(sencond):

 '''用例传fixture'''

 print("测试账号:%s, 密码:%s" % (sencond[0], sencond[1])) assert sencond[0] == "yoyo" 

if __name__ == "__main__":

 pytest.main(["-s", "test_fixture6.py"])

  

 

 

Pytest中的conftest.py  

可以有多个conftest 但是通常每个目录级别都只会有一个 这种分层的配置有直观重要的意义 ,比如系统 可以根据进入某个应用的设置通用路径

 

 

 

Pytest 运行上次失败的用例

If last-failed只重新运行上次运行失败的用例,如果没有就全部重新跑

 

pytest --lf

 

 

Ff 运行所有的测试,但是首先运行上次运行失败的测试 这可能回重新测试,从而导致fixture setup

--failed-first 

 

 

 

 

Pytest中的分布式执行。当用例比较多的时候,而每条用例都是同步的,时间比较长,可以采用分布式

pytest-xdist插件扩展了一些独特的测试执行模式pytest

测试运行并行化:如果有多个CPU或主机,则可以将它们用于组合测试运行。会加快运行速度

  • --looponfail:在子进程中重复运行测试。每次运行之后,pytest会等待,直到项目中的文件发生更改,然后重新运行以前失败的测试。
    重复此过程直到所有测试通过,之后再次执行完整运行。

多平台覆盖:您可以指定不同的Python解释器或不同的平台,并在所有平台上并行运行测试。
在远程运行测试之前,pytest有效地将您的程序源代码“rsyncs”到远程位置。报告所有测试结果并显示给您的本地终端。您可以指定不同的Python版本和解释器。

实际上就是 python中 主进程开启了多个子进程

 

pytest -n  number

 

并且xdist 可以支持html文件

pytest -n 3 --html=report.html --self-contained-html

 

 

 

重复执行用例:

    pip install pytest-repeat

Pytest-Repeat  

指定文件 重复执行 10

py.test --count=10 test_file.py

但是  如果模块中有多个case 这种方式 是单次执行10次后 在执行下个case 10

  这样可以指定 --repeat-scope 的参数 同fixture一样  有4个级别

   1 function  默认  ,范围针对每个用例重复执行,再执行下一个用例

   2 class class为用例集合单位,重复执行class  里面的用例,再执行下一个

            3 module 以模块为单位,重复执行模块里面的用例,再执行下一个

   4 session 重复真个测试会话,即所有收集的测试执行一次,然后所有这些测试再次执行等等

pytest baidu/test_1_baidu.py -s --count=5 --repeat-scope=session

 

 

  只针对的单个用例执行多次 可以在目标函数使用@pytest.mark.repeat(count)  指定次数

 

posted @ 2020-03-29 08:58  Yuan_x  阅读(343)  评论(0编辑  收藏  举报