使用环境准备
- python==3.6.7
- pycharm
- pytest 5.0.1
pytest用例规范
- 测试文件以test_开头(已_test结尾)
- 测试类以Test开头,并且不能带有init方法
- 测试函数以test_开头
- 断言使用 assert
使用pycharm编辑器,设置pytest框架 >> File >> settings >> Tools >> Python Integrated Tools >> Default test runner : pytest pycharm中pytest框架设置
快速开始
- 新建一个test_demo.py文件,写入以下代码
# test_demo.py
def test_one():
assert 1 == 2
def test_two():
assert 2 == 2
-
使用pycharm编辑器执行,它会自动识别是否使用了pytest框架
test_demo.py 执行结果
写一个测试类
- 当用例越来越多的时候,使用函数就不合适了,可以写到一个类里面
# test_class.py
class TestClass:
def test_one(self):
assert 1 == 2
def test_two(self):
assert 2 == 2
2.更改控制用例顺序
# test_class.py
import pytest
class TestClass:
def test_one(self):
assert 1 == 2
@pytest.mark.run(order=1) # 使用pytest.mark.run() 函数控制用例执行顺序
def test_two(self):
assert 2 == 2
未使用pytest.mark.run()
使用pytest.mark.run()
用例运行级别
setup_module
setup_class
setup_method
teardown_method
teardown_class
teardown_module
- setup_class/teardown_class需配合@classmethod使用
# test_pytest_object.py
import logging
import pytest
logging.basicConfig(level=logging.DEBUG)
def setup_module():
logging.info("setup_module:整个.py模块下,只执行一次")
def teardown_module():
logging.info("teardown_module:整个.py模块下,只执行一次")
class TestPytestObject2:
def test_three(self):
assert [1, 2] == [1, 3]
def test_foure(self):
assert {"a": 1, "b": "sss"} == {"a": 2, "b": "ss"}
class TestPytestObject:
@classmethod
def setup_class(cls):
logging.info("setup_class:该class下,所有用例前面执行一次")
def setup_method(self):
logging.info("setup_methon:每个用例开始前都会执行")
def test_two(self):
assert 1 == 2
def test_one(self):
assert 2 == 2
def teardown_method(self):
logging.info("teardown_method:每个用例结束后都会执行")
@classmethod
def teardown_class(cls):
logging.info("teardown_class:该class下,所有用例后面执行一次")
pytest参数化
- 使用 @pytest.mark.paramtrize() 装饰器传参
# clac.py
class Clac:
def add(self, a, b):
return a + b
def div(self, a, b):
return a/b
对Clac()方法进行参数化测试
# test_pytest_param.py
import pytest
from src.clac import Clac
class TestClac:
clac = Clac()
@pytest.mark.parametrize("a, b, c", [
(1, 2, 3),
(-1, 3, 2),
(10000, 9999, 19999),
(-1, 0, -1)
])
def test_add(self, a, b, c):
assert self.clac.add(a, b) == c
- 读取json文件传参
# test_pytest_param.py
import pytest
import json
from src.clac import Clac
class TestClac:
clac = Clac()
'''把读取json文件获得的数据,通过参数传递'''
@pytest.mark.parametrize("a, b, c", json.load(open("clac.json")))
def test_div(self, a, b, c):
assert self.clac.div(a, b) == c
- 读取yaml文件,进行数据驱动,实现参数化
- pytest框架使用yaml,很少用json,因为json格式比较严格,不支持注释;所以,我们用yaml
- 先创建一个clac.yaml文件,详细的yaml格式可以参考这个文档
# test_pytest_param.py
import pytest
import yaml
from src.clac import Clac
class TestClac:
clac = Clac()
'''把读取yaml文件获得的数据,通过参数传递'''
@pytest.mark.parametrize("a, b, c", yaml.load(open("clac.yaml")))
def test_div(self, a, b, c):
assert self.clac.div(a, b) == c
pytest fixture的使用
像上面讲到的setup和teardown可以实现在测试用例运行前后加入一些操作,但这种是整个脚本全局生效的。如果我想实现这样的场景:用例1登录,用例2不需要登录,用例3需要先登录,这样的话setup/teardown是不能满足的,这就是学习fixture的目的,自定义测试用例的预置条件
fixture的作用
- 完成setup和teardown操作,处理数据库、文件等资源的打开和关闭
- 完成大部分测试用例需要完成的通用操作,例如login、设置config参数、环境变量等
- 准备测试数据,将数据提前写入到数据库,或者通过params返回给test用例,等
fieture的优势
- 命名方式灵活,不局限于setup和teardown这几个命名
- conftest.py 配置里可以实现数据共享,不需要import就能自动找到一些配置
- scope="module" 可以实现多个.py跨文件共享前置, 每一个.py文件调用一次
- scope="session" 以实现多个.py跨文件使用一个session来完成多个用例
fixture(scope="function", params=None, autouse=False, ids=None, name=None):
'''使用装饰器标记fixture的功能
可以使用此装饰器(带或不带参数)来定义fixture功能。 fixture功能的名称可以在以后使用
引用它会在运行测试之前调用它:test模块或类可以使用pytest.mark.usefixtures(fixturename标记。
测试功能可以直接使用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.fixture(name ='<fixturename>')“”。
1. 定义一个函数级别的fixture
# -*- coding=utf-8 -*-
# 新建一个test_demo.py
import pytest
"不带参数时,默认scope=function"
@pytest.fixture()
def topics():
print("\n输入账号、密码,先登录")
def test_1(topics):
print("用例1:登录后,进行操作111\n")
def test_2():
print("用例2:无需登录")
def test_3(topics):
print("用例3:登录之后,执行其他动作222")
function级别执行结果
2. 定义一个模块级别的fixture
# -*- coding=utf-8 -*-
# 新建一个test_demo.py
import pytest
@pytest.fixture(scope="module")
def topics():
print("\n输入账号、密码,先登录")
def test_1(topics):
print("用例1:登录后,进行操作111\n")
def test_2(topics):
print("用例2:无需登录\n")
def test_3(topics):
print("用例3:登录之后,执行其他动作222")
module级别执行结果
从结果看出,module级别的fixture在当前.py模块里,只会在用例(test_1)第一次调用前执行一次
3. 定义一个session级别的fixture
- 先创建一个conftest.py文件,输入一下代码
conftest.py 配置里可以实现数据共享,当有测试用例使用pytes.fixture函数时,pytest会自动去配置文件里寻找pytest.fixture,不需要import就能自动找到一些配置
import pytest
@pytest.fixture(scope="session")
def topics():
print("\n 输入账号、密码,先登录")
创建test_fixture.py,输入一下代码
# 新建一个test_fixture.py
def test_1(topics):
print(" 用例1:登录后,进行操作111")
def test_2(topics):
print("用例2:无需登录")
创建test_fixture2.py,输入一下代码
# 新建一个test_fixture2.py
def test_3(topics):
print("\n 用例3:登录后,进行操作111")
def test_4(topics):
print("用例4:无需登录")
在Terminal窗口下,执行以下命令
(venv) F:\CAM\Appiumdemo\case>pytest -s F:\CAM\Appiumdemo\case\test_fixture.py F:\CAM\Appiumdemo\case\test_fixture2.py
session级别执行结果
从结果可以看出,session级别的fixture,在两个模块时,只会在所有用例前执行一次
yield生成器执行teardown
前面的在用例前预置条件,相当setup,在fixture里使用yield来执行teardown
网友评论