pytest

 

 

 

 

 


一、简介

1.1、 认识pytest

pytest是一个非常成熟的全功能的python测试框架,主要有以下几个特点:

  • 简单灵活,容易上手
  • 支持参数化
  • 能够支持简单的单元测试和复杂的功能测试,还可以用来做
    • pytest+selenium (webUI自动化测试)
    • pytest+appium(移动端自动化测试)
    • pytest+request(接口自动化测试)

官方文档:Pytest英文网Pytest中文网

作用:管理测试用例、执行测试用例、判断测试结果、生成测试报告

 

1.2、插件介绍

  • pytest具有很多第三方插件,并且可以自定义扩展,比较好用的如
    • pytest-selenium(集成selenium)、
    • pytest-html(完美html测试报告生成)、
    • pytest-rerunfailures(失败case重复执行)、
    • pytest-xdist(多CPU分发,多线程运行)
    • pytest-ordering(改变用例的执行顺序的插件)
    • allure-pytest(生成美观自定义的allure报告)

 

官方插件列表

 

 

二、安装

2.1、pip命令安装pytest

  1. 在命令行中运行以下命令:
pip install -U pytest
  1. 检查是否安装了正确的版本:
pytest --version
pytest 6.2.1

 

 

2.2、pip 命令安装插件

我们在项目下创建一个 requirements.txt 文件,然后把需要安装的插件写入这个文件里面

pytest
pytest-xdist
pytest-ordering
pytest-rerunfailures
allure-pytest
pyyaml
requests

在控制面版 Terminal 输入:pip install -r requirements.txt (只适用虚拟环境)

 

三、用例规则

官方文档:标准的测试发现规则

 

级别 默认定义
包级 test开头
模块 test_*.py  或   _test 结尾
类级 Test开头 且不含init方法
函数或方法级 以test_ 开头

自定义

这种规则不是一成不变的,如果因为某种需要,可以通过pytest.ini配置文件来修改命名规则, 在测试系统的顶层目录创建pytest.ini文件,在pytest.ini文件中写入如下配置:

[pytest]

# 改变规则 用例路径,.代表根目录
testpaths = ./stu

# 更改测试 文件命名规则
python_files = a*

# 更改测试类命名规则
python_classes = b*

# 更改测试函数命名规则
python_functions = c*

 

 

 

四、执行

4.1、命令行方式

在pycharm Teminnal 面板 直接输入pytest,就会执行 所有符合规则的用例

4.2、通过主函数main()

我们可以在项目的根目录下创建一个run文件,然后导入pytest包,写入main() 函数

import pytest

if __name__ == "__main__":
    pytest.main()
    # main里也可以加参数
    # pytest.main(["-vs"])
    # 指定文件执行
    pytest.main(["-vs",'setup_teatdown/test_set_teat.py::Test000::test_001'])

里面也可以加参数

4.3、通过全局配置文件pytest.ini文件执行

注:文件一定在项目根目录下,名称不可变,编码格式为ANSI ,无论命令行还是main函数运行,都会加载这个配置文件

[pytest]

# 设置一些默认参数
addopts = -vs -m 'smoke'

# 指定执行用例路径,.代表根目录
testpaths = ./stu


;# 更改测试 文件命名规则
;python_files = a*
;
;# 更改测试类命名规则
;python_classes = b*

;# 更改测试函数命名规则
;python_functions = c*


# 分组 标记
markers =
    smoke:冒烟测试用例
    sit:系统测试

注:Pycharm 里面默认运行框架 为Unittests,我们把它改为pytest

步骤:File -->Tools -->Python Integrated Tools -->Default test runner: pytest

有可能需要重启一下才生效,pytest 兼容 unittest

参数说明

参数 说明 实例
-s 输出调试信息

 -v 输出详细信息 pytest -vs
-n 多线程运行(依赖pytest.xdis插件) pytest -vs -n=5
-x 遇见用例失败 则停止测试 pytest -vs -x
-k 运行测试用例名称中包含某个字符串的测试用例 pytest -vs -k "ttt"  (运行包含ttt的测试用例,里面可以加逻辑运算)
-m 执行有标记的用例 -m "website":执行有website标记的test方法
--html 生成html的测试报告(依赖pytest-html插件) python -vs --html 报告路径(如:./reports/result.html)
--reruns 重新运行失败的用例,并指定运行最大次数 pytest --reruns 3 test00.py
--reruns-delay 用例失败重新运行时间间隔 pytest --reruns 3 --reruns-delay 1 test00.py
--maxfail 最多出现几个失败终止 pytest -vs --maxfail=2

 

 

五、前后执行组件(setup/teardown)

为了测试准备,和测试结束要做的一些事情,

测试准备:初始化代码,创建浏览器对象,创建日志对象,创建数据库连接 ,接口请求对象创建准备

测试结束:关闭所有对象

 

模块级(setup_module / teardown_module)模块始末,全局的(优先最高)
函数级(setup_function / teardown_function)只对函数用例生效(不在类中)
类级(setup_class / teardown_calss)只在类中前后运行一次 (在类中)
方法级(setup_method / teardown_methond)开始于方法始末(在类中)
类里面的(setup /teardown)运行在调用方法的前后

 

5.1、模块级:

setup_module / teardown_module

模块始末(当前文件),全局的(优先最高)

如创建一个 test_setup_teardown.py文件

#-----------模块文件----------
def setup_module():
    print("模块前运前")


def teardown_module():
    print("模块后运行")

    
def test_000():
    print("第000条用例")

5.2、函数级:

setup_function / teardown_function

只对函数用例生效(不在类中)

def setup_function():
    print("函数之前运行的代码")

def teardown_function():
    print("函数之后运行的代码")



def test_000():
    print("第000条用例")


def test_001():
    print("第001条用例")

5.3、类级:

setup_class / teardown_class

只在类中前后运行一次 (定义在类中)

class  Test000():


    def setup_class(self):
        print("在使用类前面执行")

    def teardown_class(self):
        print("在使用类后面执行")


    def test_000(self):
        print("第000条用例")


    def test_001(self):
        print("第001条用例")

5.4、方法级:

setup_method / teardown_method

开始于方法始末(在类中,和setup、teardown 功能一样,优先级稍微高一点)

class  Test000():



    def setup_method(self):
        print("类中的每一个方法前")


    def teardown_method(self):
        print("类中的每一个方法后")


    def test_000(self):
        print("第000条用例")


    def test_001(self):
        print("第001条用例")

5.5、类里面的:

setup /teardown

运行在调用方法的前后

class  Test000():


    def setup(self):
        print("方法前------")

    def teardown(self):
        print("方法后---------")


    def test_000(self):
        print("第000条用例")


    def test_001(self):
        print("第001条用例")


    def ttt(self):
        print("一个方法")

 

 

 

六、fixture (固定组件)

应用场景:可以部分用例实现,非常灵活,自定义

fixture的作用范围

fixture 里面有个scope参数可以控制fixture的作用范围:session>module>class>function

-function:每一个函数或方法都会调用

-class:每一个类调用一次,一个类中可以有多个方法

-module:每一个.py文件调用一次,该文件内又有多个function和class

-session:是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module

fixture源码详解

fixture(scope='function',params=None,autouse=False,ids=None,name=None)

scope:有四个级别参数"function"(默认),"class","module","session"

params:一个可选的参数列表,它将导致多个参数调用fixture功能和所有测试使用它。

autouse:如果True,则为所有测试激活fixture func可以看到它(自动为用例使用)。如果为False则显示需要参考来激活fixture

ids:每个字符串id的列表,每个字符串对应于params这样他们就是测试ID的一部分。如果没有提供ID它们将从params自动生成

name:fixture的名称。这默认为装饰函数的名称。如果fixture在定义它的统一模块中使用,夹具的功能名称将被请求夹具的功能arg遮蔽,解决这个问题的一种方法时将装饰函数命令"fixture_<fixturename>"然后使用"@pytest.fixture(name='<fixturename>')"。

官方文档

import pytest


class Fruit:
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        return self.name == other.name


@pytest.fixture
def my_fruit():
    return Fruit("apple")


@pytest.fixture
def fruit_basket(my_fruit):
    return [Fruit("banana"), my_fruit]


def test_my_fruit_in_basket(my_fruit, fruit_basket):
    assert my_fruit in fruit_basket

 

 

七、用例的跳过

无条件跳过

@pytest.mark.skip(reason="无理由跳过")

有条件跳过

@pytest.mark.skipif(count<5,reason="有理由跳过")

 

 

断言

unittest提供了assertEqual、assertIn、assertTrue、assertFalse。

pytest直接使用python 自带的 assert 表达式。

实 例 描 述
assert a == b
assert a != b
断言是否相等
assert a
assert False
断言真假
assert "a" in "abc"
assert "a" not in "abc"
断言是否包含
assert a is None
assert a in not None
断言是否包含
assert a is None
assert a in not None
断言是否为空
assert isinstance(a,int) 断言数据类型
def test_zero_division():
    with pytest.raises(ZeroDivisionError):
        1/0
断言异常

 

 

参数化

软件测试中,输入相应值,检查期望值,是常见测试方法。
自动化测试中,一个测试用例对应一个测试点,通常一组测试数据无法完全覆盖测试范围,所以,需要参数化来传递多组数据。


pytest的测试用例参数化使用如下装饰器即可完成。

方法:

parametrize(argnames, argvalues, indirect=False, ids=None, scope=None)

常用参数:

argnames:参数名,以逗号分隔的字符串

argvalues:参数值列表,若有多个参数,一组参数以元组形式存在,包含多组参数的所有参数

当参数为一个时格式:[value]

当参数个数大于一个时,格式为:[(param_value1,param_value2.....),(param_value1,param_value2.....)]

使用方法:

@pytest.mark.parametrize(argnames,argvalues)

️ 参数值为N个,测试方法就会运行N次

import pytest
from selenium import webdriver
from time import sleep
from selenium.webdriver.common.action_chains import ActionChains



# 一个登录案例

@pytest.mark.parametrize(
    "user,password",
    [('huang123456','****'),
     ("hduser8000","****")
     ]
)
def test_login(user,password):
    driver = webdriver.Chrome("D:\\selenium_driver\\chromedriver.exe")
    driver.implicitly_wait(10)
    driver.get("http://106.14.225.213/")
    driver.maximize_window()
    driver.find_element_by_xpath('//*[@id="main-navbar"]/div[3]/div[2]/a').click()
    driver.find_element_by_name("username").send_keys(user)
    driver.find_element_by_name("password").send_keys(password)
    driver.find_element_by_xpath('//*[@id="app"]/div/div[3]/div/form/button').click()
    sleep(3)

    tt=driver.find_element_by_xpath('//*[@id="app"]/div/div[3]/div/div/div[2]/div[1]/div[2]/div/div[2]/button/span').text
    print(tt)
    assert "发布文章" == tt
    above = driver.find_elements_by_xpath('//*[@class="vnb__menu-options__option__link"]')[1]
    ActionChains(driver).move_to_element(above).perform()
    driver.find_element_by_xpath('//*[@aria-label="退出登录"]').click()
    sleep(5)
    driver.quit()




if __name__ == '__main__':
    pytest.main(['-sv','test_par01.py'])

 

 

 

测试报告

生成junitXML 测试报告 : 这种格式的结果文件可以被Jenkins或其他CI工具解析

pytest 测试文件 --junitxml=path

生成在线测试报告

pytest 测试文件 --pastebin=all

 

 

 

 

 

 

 

参考:

https://blog.csdn.net/lovedingd/article/details/98952868

https://blog.csdn.net/zangba9624/article/details/114688271

posted @ 2022-05-08 16:32  钟鼎山林  阅读(612)  评论(0编辑  收藏  举报