Python自动化测试框架之Pytest教程

Pytest

  • pytest是一个非常成熟的全功能的Python测试框架,主要有以下几个特点:
  • 简单灵活,容易上手
  • 支持参数化
  • 能够支持简单的单元测试和复杂的功能测试,还可以用来做selenium/appnium等自动化测试、接口自动化测试(pytest+requests)
  • pytest具有很多第三方插件,并且可以自定义扩展,比较好用的如pytest-selenium(集成selenium)、pytest-html(完美html测试报告生成)、pytest-rerunfailures(失败case重复执行)、pytest-xdist(多CPU分发)等
  • 测试用例的skip和xfail处理
  • 可以很好的和jenkins集成
  • report框架----allure 也支持了pytest

安装Pytest

  • pip install -U pytest

Pytest用例设计原则

测试类以Test开头,并且不能带有init方法

以test_开头的函数

以Test开头的类

所有的包pakege必须要有__init__.py文件

断言使用assert

运行Pytest的两种方式

Pycharm里代码运行

pytest.main(["test.py"])
  • 命令行运行方式
pytest test.py
# 运行指定类下的指定方法
pytest 文件名::类名::方法名

Pytest参数说明

  • -v 说明:可以输出用例更加详细的执行信息,比如用例所在的文件及用例名称等
  • -s 说明:输入我们用例中的调式信息,比如print的打印信息等
  • -x:遇到错误的用例,立即退出执行,并输出结果
  • -v:表示查看详细的报告内容
  • -collect-only:表示把待执行的用例全部展示出来
  • -lf:只执行上次失败的用例
  • -vv :显示详细的测试结果
  • -tb=no:不展示用例失败的错误详情
  • -tb=line:展示用例失败的代码具体行数
  • -tb=short:展示更加详细的错误信息
  • -k “关键字” 说明:执行用例包含“关键字”的用例
  • -q 说明:简化控制台的输出,可以看出输出信息和上面的结果都不一样, 下图中有两个…点代替了pass结果
  • -maxfail=num 当用例错误达到指定数量时,停止测试
  • m 说明:执行特定的测试用例。我们再次修改一下我们的用例,并添加一个新的用例
     
# 如果要运行多个标识的话,用表达式,如下
pytest -m "slow or faster" test_1.py  运行有slow标识或 faster标识用例
pytest -m "slow and faster" test_1.py 运行有slow和faster标识的用例
pytest -m "slow and not faster" test_1.py 运行有slow和没有faster标识的用例

 

注意:-m后面不能带’'号(单引号),只能带“”(双引号),不然识别不到

ini配置文件

  • 创建pytest.ini文件(固定写法)
[pytest];固定写法

;变量名不能错
addopts=-vv -s ;多个参数中间空格
testpaths=../HC/huace ;多个目录中间空格
python_files=test*.py ;python文件前缀,可自定义
python_classes=huace ;指定类名
python_functions=test* ;指定方法名,可自定义

跳过测试函数

  • 跳过测试函数: 根据特定的条件,不执行标识的测试函数
# -*- coding: utf-8 -*-

import pytest

class Test():
   def test(self):
       print("执行的是testcase的用例")

@pytest.mark.skipif(condition=1<2,reason="1不大于2,所以不执行")
class huace():
   def haha(self):
       print("执行的是haha方法里面的用例")

 

Pytest之fixture

  • unittest和nose都支持fixture的,但是fixture在pytest里使用更灵活。也算是pytest的一个闪光点吧
  • 可以理解为一个跟setup和teardown这种前后置类似的东西。但是比它们要强大、灵活很多

fixtur当做参数传入

# -*- coding: utf-8 -*-

import pytest

@pytest.fixture()
def login():
   print('登录系统')

# 直接使用函数名做为参数传入
def test_01(login):
   print('测试用例一')

def test_02():
   print('测试用例2')

def test03():
   print('测试用例3')

 

运行结果

  • 只有tes_01调用了login
  • 遗留问题来了,如果我这里有10个方法或更多?是不是都需调用login方法?继续看下面的fixture参数
testcase.py::test_01 登录系统
测试用例一
PASSED
testcase.py::test_02 测试用例2
PASSED
testcase.py::test03 测试用例3
PASSED

 fixture语法

# scope有4个作用范围:function(不填则默认)、class、module、session
fixture(scope='function', params=None, autouse=False, ids=None, name=None)

参数说明

  • scope:即作用域,function"(默认),“class”,“module”,"session"四个
  • params:可选参数列表,它将导致多个参数调用fixture函数和所有测试使用它。
  • autouse:默认:False,需要用例手动调用该fixture;如果是True,所有作用域内的测试用例都会自动调用该fixture
  • ids:params测试ID的一部分。如果没有将从params自动生成.
  • name:默认:装饰器的名称,同一模块的fixture相互调用建议写个不同的name。
  • session的作用域:是整个测试会话,即开始执行pytest到结束测试scope参数作用范围控制fixture的作用范围:session>module>class>function
     

autouse

  • 参数置默认为False,则需要手动去调用装饰器
# -*- coding: utf-8 -*-

import pytest

# 当前就算定义了装饰器,也不会调用Login
@pytest.fixture()
def login():
   print("打开浏览器")

def test1():
   print("test1里的用例")

def test2():
   print("test2里的用例")

 调用方式1

# -*- coding: utf-8 -*-

import pytest

@pytest.fixture()
def login():
   print("打开浏览器")

# 直接传入函数名
def test1(login):
   print("test1里的用例")
   
def test2(login):
   print("test2里的用例")

 调用方式2

# -*- coding: utf-8 -*-

import pytest

# autouse设为True,就能自动调用login的装饰器
@pytest.fixture(autouse=True)
def login():
   print("打开浏览器")

# 直接传入函数名
def test1():
   print("test1里的用例")

def test2():
   print("test2里的用例")

function

  • function:作用域为函数
  • 所有的方法都调用了login
# -*- coding: utf-8 -*-

import pytest

@pytest.fixture(scope='function', autouse=True)
def login():
   print('登录系统')

def test_01():
   print('测试用例一')

def test_02():
   print('测试用例2')

def test03():
   print('测试用例3')

运行结果

  • 符合用例名设计的都会调用装饰器
  • login不符合所以不会调用
testcase.py::test_01 登录系统
测试用例一
PASSED
testcase.py::test_02 登录系统
测试用例2
PASSED
testcase.py::test03 登录系统
测试用例3
PASSED

 

class

  • class:作用域为类
  • 所以TestCase1和TestCase2这两个类都会执行login
# -*- coding: utf-8 -*-

# @Time : 2021/1/14 21:05
# @Author : 

import pytest

@pytest.fixture(scope='class', autouse=True)
def login():
   print('登录系统')


def test_01():
   print('这个是类外面的用例')

class TestCase1:
   def test_02(self):
       print('测试用例2')
   def test03(self):
       print('测试用例3')

class TestCase2:
   def test_04(self):
       print('测试用例4')
   def test05(self):
       print('测试用例5')

 

运行结果

  • 类里面的方法只会调用一次
  • pytest机制,因为方法是以test开头,所以也会调用
testcase.py::test_01 登录系统
这个是类外面的用例
PASSED
testcase.py::TestCase1::test_02 登录系统
测试用例2
PASSED
testcase.py::TestCase1::test03 测试用例3
PASSED
testcase.py::TestCase2::test_04 登录系统
测试用例4
PASSED
testcase.py::TestCase2::test05 测试用例5
PASSED

 

module

  • module:在当前.py脚本里面所有用例开始前只执行一次
  • 只要符合用例的设计要求,不管是类里和外边的都会调用
# -*- coding: utf-8 -*-

import pytest

@pytest.fixture(scope='class', autouse=True)
def open():
   print("打开浏览器,并且打开百度首页")

def test_s1():
   print("用例1:搜索python-1")

class TestCase():
   def test_s2(self):
       print("用例2:搜索python-2")

   def test_s3(self):
       print("用例3:搜索python-3")

 

运行结果

  • 当前文件里的用例都调用了装饰器
  • 如果类名不是为Test开头你试试看是否还会调用装饰器?
testcase.py::test_s1 打开浏览器,并且打开百度首页
用例1:搜索python-1
PASSED
testcase.py::TestCase::test_s2 打开浏览器,并且打开百度首页
用例2:搜索python-2
PASSED
testcase.py::TestCase::test_s3 用例3:搜索python-3
PASSED

session

  • fixture为session级别是可以跨.py模块调用的
  • 当我们有多个.py文件的用例时候,如果多个用例只需调用一次fixture,那就可以设置为scope=“session”,并写到conftest.py文件里
  • conftest.py文件名称是固定的,pytest会自动识别该文件。放到工程的根目录下,就可以全局调用了
  • 如果放到某个package包下,那就只在该package内有效
# -*- coding: utf-8 -*-

# conftest文件内容
import pytest

@pytest.fixture(scope="session", autouse=True)
def login():
   print("调用conftest文件的里的方法")

 两个用例文件

# -*- coding: utf-8 -*-
# testcase1.py 

import pytest

def test1():
   print("test1里的用例")

def test2():
   print("test2里的用例")
   
# -*- coding: utf-8 -*-
# testcase1.py  
import pytest
def test3():
   print("test3里的用例")

def test4():
   print("test4里的用例")

运行结果

  • 两个文件只有testcase文件的用例调了conftest里的方法
testcase.py::test1 调用conftest文件的里的方法
test1里的用例
PASSED
testcase.py::test2 test2里的用例
PASSED
testcase1.py::test3 test3里的用例
PASSED
testcase1.py::test4 test4里的用例
PASSED

pytest-allure生成测试报告

  • 安装模块:pip install allure-pytest
# 第一步:生成xml数据
pytest --alluredir=./report/xml testcase.py
# 第二步:生成html文件
allure generate --clean ./report/xml -o ./result/html

将截图加入到报告里

  • allure.attach(f, ‘图片名’, allure.attachment_type.JPG)
# -*- coding: utf-8 -*-

from selenium import webdriver
import allure

browser=webdriver.Chrome()
browser.get("https://www.baidu.com")
try:
   browser.find_element_by_id("zhiyi").send_keys('test123456')  # 输入密码,
except Exception as e:
   file_name = './test.jpg'
   browser.save_screenshot(file_name)  # 截图函数
   '''allure添加截图附件'''
   with open(file_name, mode='rb') as file:
 **加粗样式**      # 读取文件,将读取的结果作为参数传给allure
       f = file.read()  
   # 把图片添加到allure操作步骤里
   allure.attach(f, 'login', allure.attachment_type.JPG)  
   raise e

 

pytest中yield和return的区别和相同点

共同点

  • return和yield都可以返回值

区别

  • yield返回值后,后面的代码还会继续运行
  • return返回值后,后面的代码不会继续运行
# -*- coding: utf-8 -*-

import pytest

@pytest.fixture()
def openbrower():
   print("打开浏览器")
   yield "返回浏览器"
   print("关闭浏览器")

def test01(openbrower):
   print(openbrower)

 

运行结果

  • 证明yield后面的代码仍执行了
testcase.py::test01 打开浏览器
# 返回值
返回浏览器
PASSED关闭浏览器

 

usefixtures与传fixture区别

  • fixture有返回值,那么usefixture就无法获取到返回值,这个是装饰器usefixture与用例直接传fixture参数的区别。
  • 当fixture需要用到return出来的参数时,只能讲参数名称直接当参数传入,不需要用到return出来的参数时,两种方式都可以
  • @pytest.mark.usefixtures(“装饰器名”)

Pytest常用的插件

  • pytest-selenium   集成 selenium
  • pip install allure-pytest   生成漂亮的allure测试报告
  • pip install pytest-sugar   优化运行效果
  • pip install pytest-rerunfailures   执行用例失败后重新运行
  • pip install pytest-xdist   多线程并行与分布式执行
  • pip install pytest-assume   多条断言前面报错后面依然执行
  • pip install pytest-cover   测试覆盖率

一键安装多个模块

  • 创建requirement.txt文件
selenium==3.0
requests
  • pip install -r requirement.txt

 

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

 这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值