一文读懂Python之pytest
pytest官网:https://docs.pytest.org/en/stable/
pytest和unittest都是python的测试框架,但是pytest相比于unittest,又有以下特点:
- 增加了标记功能
- 有丰富的插件库,目前有800+ (点击跳转插件地址)
- 增加了fixture(可以设置会话级、模块级、类级、函数级的fixture)
- 自动发现测试模块和测试方法
- 断言方式为 assert 表达式 即可。(更加自由,表达式可以有逻辑运算可以是函数返回值)
【备注】
会话级:整个自动化用例运行过程中只执行一次的事情
模块级:每个py文件
类级:例如unittest中setUpClass和tearDownClass完成的事情
函数级:即用例级别,例如unittest中setUp和tearDown完成的事情
pytest安装
1、安装(-U即--upgrade,意思是如果已安装,则升级到最新版)
pip install -U pytest
2、检查安装是否成功
$ pytest --version
pytest 6.1.2
pytest自动收集测试用例的规则
1、在哪个目录下运行pytest命令,则在那个目录下搜索测试用例
2、test_*.py文件 或者 *_test.py 文件中:
(1)以test_*开头的函数名
(2)以Test开头的测试类(不能有__init__函数)中,以test_*开头的函数
上述(1)(2)是或的关系。因为py文件中可以直接写函数,或者先写类,再在类中写函数。
为什么类不能有__init__函数:
__init__是一个类的构造函数,但是为什么不能有它我也不知道。但是有的话,确实会报错。
pytest标记
在测试用例/测试类前面加上: @pytest.mark.标记名
可以在一个用例上面加多个标记。标记名可以自己取。
@pytest.mark.main #该类下的所有用例都有该标记
class TestLogin:
@pytest.mark.demo @pytest.mark.smoke
@pytest.mark.skip
def test_login_success(): pass
def test_login_success():
pass
需要运行的目录下添加pytest.ini配置文件,在其中给标记注册。如下:
[pytest] markers = smoke: smoke case skip: skip case
pytest命令
$ pytest #直接收集用例并执行
$ pytest --help #帮助
$ pytest -m smoke #仅执行标记了smoke的测试用例
执行命令:
-q :更简洁的报告模式。Execute the test function with “quiet” reporting mode。
-v :更详细的报告模式;
-m :仅执行满足mark表达式的测试用例。only run tests matching given mark expression。示例:pytest -m 'mark1 and not mark2'.
pytest之fixture
fixture:即测试用例执行的环境准备和清理;在unittest中体现为:setUp/tearDown/setUpClass/tearDownClass;
fixture主要的目的是为了提供一种手段去执行重复的、基本的内容。
例如:测试某个电商网站的功能,登录和退出就可以用到fixture,几乎每个用例都需要登录和退出。
定义fixture:在函数声明前加上标记 @pytest.fixture 在函数内部通过yield关键字分割环境准备和清理。
可选范围:function(默认), class, module, package, session
@pytest.mark.usefixtures('') 可以叠放使用,但是先执行底层的,再执行上层的。下面的例子中先执行reresh_page,再执行login;
conftest.py文件专门用来写公用的fixture,不需要引用,直接用标记即可使用。(必须和test_*.py在一个层级,或位于它的父级)
conftest可以按路径写多个。使用时有就近原则。
1 driver = None 2 @pytest.fixture(scope='class') 3 def login(): 4 #前置 5 global driver 6 driver = webdriver.Chrome() 7 lg = ... 8 yield (lg,driver) #分割线 + 返回值 9 #后置 10 11 12 @pytest.fixture(scope='function') 13 # 默认就是函数级别。也可以简写成:@pytest.fixture 14 # scope:"function"(default), "class", "module", "package" or "session" 15 def reresh_page(): 16 global driver 17 yield #分割线 18 driver.refresh()
1 #使用fixtures。 2 #作用域不一样login只会执行一次,reresh_page执行次数和用例一样。 3 @pytest.mark.usefixtures("login") 4 @pytest.mark.usefixtures("reresh_page") 5 class TestLoging: 6 7 def test_login_success(self,login): #fixture的函数名称,用来接收它的返回值 8 login[1].find_element... 9 ... 10 11 def test_error_pwd(self,login): 12 pass
pytest之断言
直接使用assert 。不再使用unittest的self.assertTrue()之类的方法。
1 #self.assertTrue(IndexPage(self.driver).is_exit_exist()) 2 #pytest直接用assert. 3 #assert后面的值要么是True要么是False 4 #assert后面的值还可以参与逻辑运算,可以是函数返回值 5 assert IndexPage(self.driver).is_exit_exist()
pytest之ddt的实现
使用方法:@pytest.mark.parametrize('参数名',列表数据)
#参数名:用来接收每一项数据,并且作为测试用例的参数
#列表数据:一组数据
@pytest.mark.parametrize("p_data",LD.phone) def login(p_data): #函数名称中必须当成参数传递过来 pass
pytest之插件
1、重运行机制:pytest-rerunfailures
#安装
pip install pytest-rerunfailures
#使用
$ pytest --reruns 5 --reruns-delay 1
--reruns 重运行次数
--reruns-delay 上一次执行完后等待多久再次重运行。单位:秒
2、html测试报告:pytest-html
#安装
pip install pytest-html
#使用 只能配置相对路径
$ pytest --html=report\\report.html
3、html测试报告:pytest-testreport
#安装 pip install pytest-testreport #使用-可配置参数template指模板(1、2) pytest --report=report.html --title=测试报告 --tester=testername --desc=项目描述 --template=1 #若使用时报错,No module named 'jinja2',则需安装 pip install jinja2
拓展:
pytest自带的报告:--junit-xml = report.xml (可以通过pytest --help命令看见) 可以集成到jenkins
报错及解决方案
使用pytest的报错记录
1、
[9580:13912:0618/213000.930:ERROR:device_event_log_impl.cc(214)] [21:30:00.929] Bluetooth: bluetooth_adapter_winrt.cc:1074 Getting Defau
lt Adapter failed.
处理方案:需要加上options
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-logging"])
services = Service(executable_path=getpath.chromedriver_path)
driver = webdriver.Chrome(options=options,service=services)
2、 TypeError: 'TestIndex' object is not subscriptable
处理方案:写测试类时,类方法第一个参数代表自己,必须是self,后面才是从fixture接收的参数;
3、