编写规范:

• 测试⽂件以 test_ 开头(以 test 结尾也可以)
• 测试类以 Test 开头,并且不能带有 init 方法!
• 测试函数以 test

模块级(setup_module/teardown_module)模块始末,全局的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import pytest
"""
开始于模块始末,全局的
setup_module
teardown_module
"""

def setup_module():
print "setup_module():在模块最之前执行\n"

def teardown_module():
print "teardown_module:在模块之后执行"

def setup_function():
print "setup_function():每个方法之前执行"

def teardown_function():
print "teardown_function():每个方法之后执行\n"

def test_01():
print "正在执行test1"
x = "this"
assert 'h' in x

def add(a,b):
return a+b

def test_add():
print "正在执行test_add()"
assert add(3,4) == 7
#运行结果:setup_module --> setup_function --> test_01--> teardown_function --> setup_function --> test_add()--> teardown_function --> teardown_module

函数级(setup_function/teardown_function)只对函数用例生效,而且不在类中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import pytest

def setup_function():
print "setup_function():每个方法之前执行"

def teardown_function():
print "teardown_function():每个方法之后执行"

def test_01():
print "正在执行test1"
x = "this"
assert 'h' in x

def test_02():
print "正在执行test2"
x = "hello"
assert hasattr(x,"hello")

def add(a,b):
return a+b

def test_add():
print "正在执行test_add()"
assert add(3,4) == 7

if __name__=="__main__":
pytest.main(["-s","test_function.py"])

#运行结果为:(-s为了显示用例的打印信息 -q只显示结果不显示过程)
#可以看出执行的结果是:
#setup_function--》 test_01 --》teardown_function
#setup_function--》 test_02 --》teardown_function
#setup_function--》 test_add --》teardown_function

类级(setup_class/teardown_class)在类中使用,类执行之前运行一次,类执行之后运行一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import pytest

class TestClass(object):

def setup_class(self):
print "setup_class(self):每个类之前执行一次"

def teardown_class(self):
print "teardown_class(self):每个类之后执行一次"

def add(self,a,b):
print "这是加法运算"
return a+b

def test_01(self):
print "正在执行test1"
x = "this"
assert 'h' in x

def test_add(self):
print "正在执行test_add()"
assert self.add(3, 4) == 7

#执行顺序 setup_class --》 test1 --》test_add()--》teardown_class

⽅法级(setup_method/teardown_methond)运⾏在类中⽅法始末

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import pytest

class TestMethod(object):

def setup_class(self):
print "setup_class(self):每个类之前执行一次\n"

def teardown_class(self):
print "teardown_class(self):每个类之后执行一次"

def setup_method(self):
print "setup_method(self):在每个方法之前执行"

def teardown_method(self):
print "teardown_method(self):在每个方法之后执行\n"

def add(self,a,b):
print "这是加法运算"
return a+b

def test_01(self):
print "正在执行test1"
x = "this"
assert 'h' in x

def test_add(self):
print "正在执行test_add()"
assert self.add(3, 4) == 7
#执行结果: setup_class --》 setup_method -->test1 -->teardown_method --》setup_method --> test_add()--》teardown_method --> teardown_class

执行

pytest/py.test(终端,命令⾏,pycharm都⾏,可配置pycharm使⽤pytest⽅式执⾏)
pytest –v (最⾼级别信息—verbose)
pytest -v -s ⽂件名 (s是带控制台输出结果,也是输出详细)
pytest将在当前⽬录及其⼦⽬录中运⾏test _ * .py或* test.py形式的所有⽂件。
以test_开头的函数,以Test开头的类,以test_开头的⽅法。所有包package都要有init.py⽂件。
Pytest可以执⾏unittest框架写的⽤例和⽅法

1
2
3
4
5
6
7
8
9
10
11
12
# 执行一个module
pytest -v src/testcases/api/test_autouse.py
# 执⾏⼀个类,一个方法
pytest -v src/testcases/api/test_autouse.py::TestSample
pytest -v src/testcases/api/
test_autouse.py::TestSample::test_answer
# 执⾏⼀个目录或者package
pytest -v src/testcases/api!
# 通过标签来运行测试用例
pytest -m P0 src/testcases/api/
# 通过pytest.main来执行,所有的参数和pytest命令行方式一样
pytest.main(['-v', '--instafail', 'testcases/api/test_example.py', '-m=P2'])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# pytest执⾏⽤例出错时停⽌
pytest -x -v -s test.py --maxfail=2
# pytest通过⽂件名类名⽅法及组合调⽤部分⽤例执
# 直接输⼊⽂件名,类名
pytest test_class_01.py
pytest -v -s test_class_01.py
pytest -v test_class_01.py::TestClass
pytest -v test_class_01.py::TestClass::test_one
# 使⽤-k TestClass是类名,and是运算符,还可以是and not…,test_one是⽅法名中含有的信息
pytest -k "TestClass and test_one"
pytest -k "TestClass or test_one"
# mark标记执⾏
# 在测试⽤例⽅法上加@pytest.mark.webtest,-s参数: 输出所有测试⽤的print信息 -m:执⾏⾃定义标记的相关⽤例
pytest -s test.py -m=webtest

log

1
2
3
4
5
6
7
8
9
建立这个文件Pytest.ini
[pytest]
log_cli = 1
log_cli_level = info
log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)
log_cli_date_format=%Y-%m-%d %H:%M:%S
写测试用例————log.info()
用: pytest pytest_testlog.py -o log.cli=true -o log._cli_level=INFO
-o是覆盖配置的内容

插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# pytest-html ⽣成可视化报告,--self-contained-html(把css样式合并到html)
pytest -v -s --html=report.html --self-contained-html

# pytest-assume 多条断⾔有失败也都运⾏
pytest.assume(1==4)
pytest.assume(2==4)

# pytest-rerunfailures 失败重跑 —reruns n, n是重复次数
pytest --reruns 5 test_sort_demo.py

pytest-allure #⾼⼤上精美报告

pytest-xdist #并⾏运⾏多个测试 pytest -n 2 在2个cpu上运⾏测试 —looponfail标志,它将⾃动重新运⾏你的失败测试
pytest -n 3

Pytest-sugar # 改变pytest的默认外观,增加进度条,安装后即可

Pytest-picked # 运⾏基于你已修改但尚未提交给git的代码的测试。

Pytest-instafail # 修改默认⾏为,以⽴即显⽰失败和错误,⽽不是等到pytest完成每个测试运⾏。

Pytest-tldr # 简化man指令帮助文档

Pytest-django # 开发web

Pytest-selenium # pytest 提供运⾏⽀持selenium为基础

pytest-ordering # 顺序编排
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# fixtures:为了测试⽤例的执行,⽽初始化⼀些数据和方法
# 名字调用,yield 之前相当于setup;yield 之后相当于tearDown
import pytest
@pytest.fixture()
def loginandlogout():
print('do login action\n')
yield
print("do logout action")
class TestSample:
def test_answer(self,loginandlogout):
...
def test_answer2(self,loginandlogout):
...
# decorator调用
import pytest
@pytest.fixture()
def loginandlogout():
print('do login action\n')
yield
print("do logout action")
class TestSample:
@pytest.mark.usefixtures('loginandlogout')
def test_answer(self):
...
# autouse,Fixture scope(module,session,class,package)
@pytest.fixture(scope='module',autouse=True)
def loginandlogout():
print('do login action\n')
yield
print("do logout action\n")
@pytest.fixture(scope='class',autouse=True)
def clickhome():
print('click home button\n')
yield
print("end click home link\n")
class TestSample:
def test_answer(self):
def test_answer2(self):
class TestSampleTwo:
def test_two_answer(self):
# 带参数传递
@pytest.fixture(params=[1, 2, 3, 'bq'])
# mark.parametrize参数化
@pytest.mark.parametrize()
# 参数化和数据驱动
@pytest.mark.parametrize(“login_r”,test_user_data, indirect=True) indeirect=True是把login_r当作函数去执⾏
# mark中的skip与xfail
# 跳过这个测试⽤例,可以加条件skipif,在满⾜某些条件下才希望通过,否则跳过这个测试。
@pytest.mark.skip
# 测试通过时尽管预计会失败(标记为pytest.mark.xfail),它是⼀个xpass,将在测试摘要中报告。
@pytest.mark.xfail


# Conftest.py:定义共享的fixture,一般放在testcases目录下,每个子目录下的conftest.py中的fixture优先

钩子函数

获取用例执行结果处理用例依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# conftest.py

import pytest

# 定义一个类,来存储用例执行失败的结果
class Falied:
skip = False


@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_makereport():
result = yield
report = result.get_result()

# 当前用例名
case_name = report.nodeid.split('::')[-1]

# 当某个用例失败时(setup失败也算)或被跳过时,将这个用例的执行结果存储在 Failed 类中
if report.when in ('setup', 'call') and report.outcome in ('failed', 'skipped'):
setattr(Falied, case_name, True)

if __name__ == '__main__':
pytest.main(['-s', '-q'])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pytest
from .conftest import Falied


class TestDemoA:

def test_A_001(self):
# 断言失败
assert 0

def test_A_002(self):
# 从 Failed 获取 test_A_001 的执行结果
if getattr(Falied, 'test_A_001', False):
pytest.skip('test_A_001 执行失败或被跳过,此用例跳过!')

def test_A_003(self):
pass

report

生成 JunitXML 格式的测试报告,命令 : –junitxml= path
生成 ResultLog 格式的测试报告,命令: –resultlog=report/log.txt
生成 Html 格式的测试报告,命令: –html=OutPuts/reports/report.html (相对路径)

1
pytest --html=report/html/report.html  testcase/test_new_outfit.py::TestNewOutfit::test_newoutfit_normal