标签:closed ams hide 测试文件 eth str 很多 erro 场景
pip install -U pytest -i https://pypi.tuna.tsinghua.edu.cn/simple
Pytest默认能够自动识别test开头的函数和方法,以及Test开头的类
import pytest import requests def test_one(): r = requests.get(‘https://itcast.cn‘) print(r.status_code) def test_two(): r = requests.get(‘https://itcast.cn‘) print(r.encoding) class TestTask(object): def test_1(self): print(‘test 1‘) def test_2(self): print(‘test 2‘) if __name__ == ‘__main__‘: pytest.main() # pytest
通过代码中pytest.main()可以右键直接运行,相当于python test_1.py,也可以通过命令行的方式启动运行
断言使用的场景
采用assert断言时,可以添加备注信息
当断言预测失败时,备注信息会以AssertionError抛出,并在控制台输出
assert expression [, arguments] # expression 为 True 则 pass # expression 为 False 则 抛出异常,有 argument 则输出 argument ‘‘‘ expression: 1)比较运算 2)逻辑运算 and | or | not 3)身份运算 is | is not 4)成员运算 in | not in ‘‘‘
示例代码:
import requests def test_assert(): r = requests.get(‘http://www.itcast.com‘) assert r.status_code == 200, "没有返回200,断言失败"
在测试过程中,对某些方法进行测试时,预测输入某些特定数据,会抛出某种异常,若出现该异常,则用例执行通过。
对这预期会出现的异常的断言,可以采用pytest中的pytest.raises()
进行处理。
pytest.raises(expected_exception, match=None, kwargs)
import pytest def is_leap_year(year): # 先判断year是不是整型 if isinstance(year, int) is not True: raise TypeError("传入的参数不是整数") elif year == 0: raise ValueError("公元元年是从公元一年开始!!") elif abs(year) != year: raise ValueError("传入的参数不是正整数") elif (year % 4 == 0 and year % 100 != 0) or year % 400 == 0: print("%d年是闰年" % year) return True else: print("%d年不是闰年" % year) return False class TestAssert(object): """对判断是否是闰年的方法进行测试""" def test_exception_typeerror(self): # 使用 pytest.raises 作为上下文管理器,捕获给定异常类型TypeError # is_leap_year(‘2020‘)抛出TypeError,被pytest.raises捕获到,则测试用例执行通过 with pytest.raises(TypeError): # 传入字符串引发类型错误 is_leap_year(‘2020‘)
有时候我们可能需要在测试用到产生的异常信息,我们可以把异常信息存储到一个变量中,变量的类型为 异常类 ,包含异常的 type、value 或者 traceback 等信息
import pytest def is_leap_year(year): # 先判断year是不是整型 if isinstance(year, int) is not True: raise TypeError("传入的参数不是整数") elif year == 0: raise ValueError("公元元年是从公元一年开始!!") elif abs(year) != year: raise ValueError("传入的参数不是正整数") elif (year % 4 == 0 and year % 100 != 0) or year % 400 == 0: print("%d年是闰年" % year) return True else: print("%d年不是闰年" % year) return False class TestAssert(object): """对判断是否是闰年的方法进行测试""" def test_exception_typeerror(self): # 预测到参数不符合要求,会抛出TypeError异常;若出现该异常,则测试用例执行通过 with pytest.raises(TypeError) as err_info: # 传入字符串引发类型错误 is_leap_year(‘2020‘) # 断言异常的类型是 TypeError,断言成功则不会抛出异常 assert err_info.type == TypeError, ‘错误类型不匹配‘
import pytest def is_leap_year(year): # 先判断year是不是整型 if isinstance(year, int) is not True: raise TypeError("传入的参数不是整数") elif year == 0: raise ValueError("公元元年是从公元一年开始!!") elif abs(year) != year: raise ValueError("传入的参数不是正整数") elif (year % 4 == 0 and year % 100 != 0) or year % 400 == 0: print("%d年是闰年" % year) return True else: print("%d年不是闰年" % year) return False class TestAssert(object): """对判断是否是闰年的方法进行测试""" def test_exception_typeerror(self): # 通过异常的内容捕捉异常,但具体是何种异常不清楚 with pytest.raises(Exception, match=‘从公元一年开始‘) as err_info: is_leap_year(0) # 断言异常的类型是 ValueError,断言成功则不会抛出异常 assert err_info.type == ValueError
pytest.warns(expected_warning, *args, match, **kwargs)
expected_warning
预期的警告类或者警告类元组,例如:
match:支持正则表达式,可以自定义的匹配警告内容
抛出异常方式:
import warnings warnings.warn(message, category)
说明:
import pytest import warnings def make_warn(): # 抛出 warnings.warn("deprecated", DeprecationWarning) def not_warn(): pass def user_warn(): warnings.warn("user warn", UserWarning) class TestWarns(object): def test_make_warn(self): with pytest.warns(DeprecationWarning): make_warn() def test_not_warn(self): # 断言not_warn函数将会抛出警告 # 但实际是没有抛出警告的,所以无法通过测试 with pytest.warns(Warning): not_warn() def test_user_warn(self): with pytest.warns(UserWarning): user_warn()
将告警信息存入一个变量中,通过读取这个变量中的信息进行断言,包括:告警的个数、告警信息参数等。
# pytest_code/test_8.py import warnings import pytest def warn_message(): warnings.warn("user", UserWarning) warnings.warn("runtime", RuntimeWarning) def test_warn_match(): with pytest.warns((UserWarning, RuntimeWarning)) as record: warn_message() assert len(record) == 2 assert str(record[0].message) == "user" assert str(record[1].message) == "runtime" assert record[0].category == UserWarning assert record[1].category == RuntimeWarning
3.3.3、通过警告内容捕捉警告
# pytest_code/test_9.py import warnings import pytest def make_warn(): # 抛出 warnings.warn("deprecated", DeprecationWarning) def not_warn(): pass def user_warn(): warnings.warn("user warn", UserWarning) class TestWarns(object): def test_make_warn(self): # 捕获警告内容为deprecated的警告 with pytest.warns(Warning, match=‘deprecated‘): make_warn()
setup_function()
和teardown_function()
函数定义在类外,会对test开头的非类方法(普通测试用例函数)生效
作用域单个测试用例,每次在单个用例执行前后都执行
import pytest def setup_function(): print("setup_function(): 每个方法之前执行") def teardown_function(): print("teardown_function(): 每个方法之后执行") def test_01(): print("正在执行test1") def test_02(): print("正在执行test2") class TestClass(object): def test_03(self): print("正在执行 类方法 test3") def normal_func(): print("正在执行normal_func")
必须在类中定义,只作用于本类
setup_class
和 teardown_class
会在在类执行前后执行一次
setup_method
和 teardown_method
会在每个测试方法前后执行一次import pytest class TestMethod(object): @classmethod def setup_class(cls): print("setup_class(self):每个类之前执行一次") @classmethod def teardown_class(cls): print("teardown_class(self):每个类之后执行一次") def setup_method(self): print("setup_method(self):在每个方法之前执行") def teardown_method(self): print("teardown_method(self):在每个方法之后执行\n") def test_01(self): print("正在执行test1") def test_02(self): print("正在执行test2") def normal_func(self): print("正在执行normal_func")
定义在类外,作用域本模块
setup_module
和 teardown_module
会模块内只执行一次,和函数级别,类级别不冲突
import pytest def setup_module(): print("setup_module():在模块最之前执行") def teardown_module(): print("teardown_module:在模块之后执行") def setup_function(): print("setup_function():每个方法之前执行") def teardown_function(): print("teardown_function():每个方法之后执行") def test_outside_1(): print(‘正在执行test_outside_1‘) def test_outside_2(): print(‘正在执行test_outside_2‘) class TestMethod(object): @classmethod def setup_class(cls): print("setup_class(self):每个类之前执行一次") @classmethod def teardown_class(cls): print("teardown_class(self):每个类之后执行一次") def setup_method(self): print("setup_method(self):在每个方法之前执行") def teardown_method(self): print("teardown_method(self):在每个方法之后执行\n") def test_01(self): print("正在执行test1") def test_02(self): print("正在执行test2") def normal_func(self): print("正在执行normal_func")
setup_module()
teardown_module()
setup_class(self)
teardown_class(self)
setup_method(self)
teardown_method(self)
setup_function()
teardown_function()
例1
需要登录,用例2
不需要登录,用例3
需要登录,这就无法直接用setup和teardown来同一个类中实现,却可以通过pytest.fixture
实现。fixture是pytest特有的功能,它以装饰器形式定义在函数上面, 在编写测试函数的时候,可以将被fixture装饰的函数的名字做为测试函数的参数,运行测试脚本时,执行测试函数时就会自动传入被fixture装饰的函数的返回值。
import pytest import requests # 0.@pytest.fixture装饰函数 @pytest.fixture() def get_web_url(): print(‘get_web_url‘) return ‘https://www.baidu.com‘ # 1. 把上面函数名作为测试用例的参数 def test_web(get_web_url): # 2. 测试用例调用前,需要先确定形参get_web_url,就是调用get_web_url print(‘test_web‘) print(get_web_url) # 测试用例内部使用get_web_url,就是使用它返回值 r = requests.get(get_web_url) assert r.status_code == 200, ‘测试失败‘
conftest.py
文件中,所需的fixture对象会自动被pytest
发现,而不需要再每次导入
conftest.py
文件名固定
在conftest.py文件中实现共用的fixture函数
conftest.py代码:
# pytest_fixture/conftest.py 文件名不能改变,否则无效 import pytest # 默认是function级别的 @pytest.fixture() def login_fixture(): """可以把函数作为参数传递""" print("\n公用的登陆方法")
测试用例代码:
# pytest_fixture/test_fixture1.py import pytest def test_get_carts(): """购物车不需要登陆""" print("\n测试查询购物车,无需登录") class TestFixtures(object): """需要登陆的信息""" def test_get_user_info(self, login_fixture): print("获取用户信息") def test_order_info(self, login_fixture): print("查询订单信息") def test_logout(login_fixture): """登出""" print("退出登录") if __name__ == ‘__main__‘: pytest.main([‘-s‘, ‘test_fixture1.py‘])
pytest.fixture(scope=‘function‘, params=None, autouse=False, ids=None, name=None)
# function:设置为function,表示每个测试方法都要执行一次 import pytest @pytest.fixture(scope=‘function‘) # @pytest.fixture() # 和上面等价 def foo(): print(‘foo‘) def test_1(foo): print(‘普通测试用例111111‘) def test_2(): print(‘普通测试用例22222‘) class TestClass(object): def test_one(self, foo): print(‘类实例方法测试用例111111‘) def test_two(self, foo): print(‘类实例方法测试用例22222‘)
import pytest @pytest.fixture(scope=‘class‘) def foo(): print(‘foo‘) def test_1(foo): print(‘普通测试用例111111‘) def test_2(foo): print(‘普通测试用例22222‘) class TestClass(object): def test_one(self, foo): print(‘类实例方法测试用例111111‘) def test_two(self, foo): print(‘类实例方法测试用例22222‘)
# module:只会在最开始的时候传入参数执行1次 # session:只会在session开始传入参数的时候执行1次 import pytest @pytest.fixture(scope=‘module‘) # @pytest.fixture(scope=‘session‘) def foo(): print(‘foo‘) def test_1(foo): print(‘普通测试用例111111‘) def test_2(foo): print(‘普通测试用例22222‘) class TestClass(object): def test_one(self, foo): print(‘类实例方法测试用例111111‘) def test_two(self, foo): print(‘类实例方法测试用例22222‘)
案例一:
import pytest def check_password(password): """ 检查密码是否合法 :param password: 长度是 8 到 16 :return: """ pwd_len = len(password) if pwd_len < 8: return False elif pwd_len > 16: return False else: return True @pytest.fixture(params=[‘1234567‘, ‘12345678‘, ‘123456789‘, ‘123456789012345‘, ‘1234567890123456‘, ‘12345678901234567‘]) def password(request): return request.param def test_check_password(password): print(password) print(check_password(password))
案例二:
import pytest @pytest.fixture(params=[‘admin‘, ‘zhangsan‘, ‘lisi‘]) def username(request): return request.param @pytest.fixture(params=[‘1234567‘, ‘12345678‘, ‘123456789‘, ‘123456789012345‘, ‘1234567890123456‘, ‘12345678901234567‘]) def password(request): return request.param def test_check_regist(username, password): print(username, ‘=====‘, password) if __name__ == ‘__main__‘: pytest.main([‘-s‘, ‘test_14_params2.py‘])
pytest.fixture(autouse=False) 的autouse参数默认为False, 不会自动执行;设置为True时,当前运行的所有测试函数在运行前都会执行fixture函数
import pytest @pytest.fixture(autouse=True) def before(): print(‘\nbefore each test‘) class Test2: def test_1(self): print(‘test_5‘) def test_2(self): print(‘test_6‘)
pytest.mark下提供了标记装饰器,以下是一些常用的标记装饰器
装饰器 | 作用 |
pytest.mark.xfail() | 将测试函数标记为预期失败 |
pytest.mark.skip() | 无条件地跳过测试函数 |
pytest.mark.skipif() | 有条件地跳过测试函数 |
pytest.mark.parametrize() |
参数化Fixture方法和测试函数 |
pytest.mark.usefixtures() |
使用类、模块或项目中的Fixture方法 |
可以使用@pytest.mark.usefixtures(‘fixture函数名字符串‘)
来装饰测试类和测试方法
测试用例:
import pytest def test_get_carts(): """购物车不需要登陆""" print("\n测试查询购物车,无需登录") @pytest.mark.usefixtures(‘login_fixture‘) class TestFixtures(object): """需要登陆的信息""" def test_get_user_info(self): print("获取用户信息") def test_order_info(self): print("查询订单信息") @pytest.mark.usefixtures(‘login_fixture‘) def test_logout(): """登出""" print("退出登录") if __name__ == ‘__main__‘: pytest.main([‘-s‘, ‘test_fixture2.py‘])
注意:这种方法无法使用fixture装饰的函数的返回值,将被fixture装饰的函数的名字作为测试用例函数的参数这种方式可以使用fixture装饰的函数的返回值
xfail(condition=True, reason=None, raises=None, run=True, strict=False)
import pytest class Test_ABC: def setup_class(self): print("\nsetup") def teardown_class(self): print("\nteardown") def test_a(self): print("\ntest_a") @pytest.mark.xfail(condition=False, reason="预期不会失败") def test_b(self): print("\ntest_b") assert 0 @pytest.mark.xfail(condition=True, reason="预期失败") def test_c(self): print("\ntest_c") assert 0 if __name__ == ‘__main__‘: pytest.main([‘-s‘, ‘test_22.py‘])
使用场景: 根据特定条件、不执行标识的测试函数
skip(reason=None)
import pytest import pytest class Test_ABC: def setup_class(self): print("\nsetup") def teardown_class(self): print("\nteardown") def test_a(self): print("test_a") # 开启跳过标记 @pytest.mark.skip(reason="无条件跳过不执行,就是任性顽皮") def test_b(self): print("test_b") if __name__ == ‘__main__‘: pytest.main([‘-s‘, ‘test_23.py‘])
使用场景: 根据特定条件、不执行标识的测试函数
skipif(condition, reason=None)
import pytest class Test_ABC: def setup_class(self): print("\nsetup") def teardown_class(self): print("\nteardown") def test_a(self): print("test_a") # 开启跳过标记 @pytest.mark.skipif(condition=1, reason="有条件跳过不执行,依旧任性顽皮") # @pytest.mark.skipif(condition=0, reason="条件跳不成立,无法跳过") def test_b(self): print("test_b") if __name__ == ‘__main__‘: pytest.main([‘-s‘, ‘test_24.py‘])
使用场景:需要测试一组不同的数据,而测试过程是一样的,这种情况下我们可以写一个测试方法,并且测试方法通过参数接受数据。通过遍历数据并且调用测试方法来完成测试。
作用: 参数化fixture方法和测试函数, 方便测试函数对测试属性的获取。
parametrize(argnames, argvalues, indirect=False, ids=None, scope=None)
[(param_value1,param_value2),...]
import pytest class Test_ABC: def setup_class(self): print("setup") def teardown_class(self): print("teardown") def test_a(self): print("test_a") @pytest.mark.parametrize("a", [3, 6]) def test_b(self, a): print(f"test_b data:a={a}") @pytest.mark.parametrize(["a","b"],[(1,2),(3,4)]) def test_c(self, a, b): print(f"test_c a: {a}; b: {b}") if __name__ == ‘__main__‘: pytest.main([‘-s‘, ‘test_25.py‘])
标签:closed ams hide 测试文件 eth str 很多 erro 场景
原文地址:https://www.cnblogs.com/yycnbolg/p/13854108.html