python自动化web自动化:1.pytest框架讲解+集成allure

Posted new nm个对象

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python自动化web自动化:1.pytest框架讲解+集成allure相关的知识,希望对你有一定的参考价值。

一.什么是pytest

我们在编写自动化脚本时,需要一套框架来管理我们的测试用例。这样我们的用例才会更加灵活,pytest就是这么一个框架。

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

  • 简单灵活,容易上手,文档丰富;
  • 支持参数化,可以细粒度地控制要测试的测试用例;
  • 能够支持简单的单元测试和复杂的功能测试,还可以用来做selenium/appnium等自动化测试、接口自动化测试(pytest+requests);
  • pytest具有很多第三方插件,并且可以自定义扩展,比较好用的如pytest-selenium(集成selenium)、pytest-html(完美html测试报告生成)、pytest-rerunfailures(失败case重复执行)、pytest-xdist(多CPU分发)等;
  • 测试用例的skip和xfail处理;
  • 可以很好的和CI工具结合,例如jenkins

二.安装pytest

pytest是python的第三方开源库,需要安装后才能使用

pip install pytest

三.pytest识别用例

pytest是用来管理我们测试用例的,那么它就需要知道代码中哪些是测试用例。pytest有一套专门的规范来识别用例。

  • 识别python文件:我们的python文件以“test*”格式命令,pytest会自动识别为用例文件
  • 识别类:我们的类以“Test*”格式命令,pytest会自动识别为用例类
  • 函数(方法):我们的函数以“test*”格式命令,pytest会自动识别为用例方法
    在这里插入图片描述
    补充:自定义命令规范
    pytest匹配用例的规范,我们也可以自己定义。
  • 在项目根目录下插件pytest.ini文件
  • 在pytest.ini文件中定义匹配规则
    在这里插入图片描述

四.pytest运行用例的几种方式

1.pycharm工具中使用pytest运行

第一步:先在pycharm中设置pytest为用例执行器。

File | Settings | Tools | Python Integrated Tools

在这里插入图片描述
第二步:使用pytest运行用例
在这里插入图片描述
注意:这种方式只能在pycharm开发工具中使用,平时开发过程中使用这种方式会很方便

2.在python代码中指定使用pytest运行

在这里插入图片描述

3.在终端使用pytest命令运行

python -m pytest test_baidu_search.py
# 因为pytest是python中的一个库,所以前面需要使用python -m来指定pytest
# 后面如果不加test_baidu_search.py这个文件名,则pytest会自动在当前目录下配置所以符合pytest命令规划的文件

五.运行pytest常用参数

  1. pytest + 测试用例模块路径---------执行对应模块下的测试用例
pytest test_clac.py
# 执行test_clac.py文件下的所有以test开头的方法,以及以Test开头的类,且在类中以test开头的方法。
  1. pytest -v + 测试用例模块路径-----执行对应模块下的测试用例,并且打印详细日志

    pytest -v test_clac.py
    
  2. pytest -s + 测试用例模块路径-----执行对应模块下的测试用例,并且打印控制台消息(及会打印用例中,print的内容)

    pytest -s test_clac.py
    
  3. pytest -k ‘正则表达式’ + 测试用例模块路径-----执行对应模块下的能被正则表达式匹配到的测试用例

    pytest -v -k 'jia' test_clac.py--------执行test_clac.py文件下,用例名称含‘jia’的测试用例,并且打印详细日志
    pytest -v -k 'jia or cheng' test_clac.py----执行test_clac.py文件下,用例名称含‘jia’或者含‘cheng’的测试用例,并且打印详细日志
    pytest -v -k 'not cheng' test_clac.py------执行test_clac.py文件下,用例名称含‘cheng’以外的所有测试用例,并且打印详细日志
    
  4. pytest test_clac.py --collect-only-----收集test_clac.py下的所有测试用例,但不执行

  5. pytest -m add test_clac.py---------执行test_clac.py下的所有有add标签的测试用例

  6. pytest -v test_clac.py --junit-xml=./result-----执行test_clac.py下的所有测试用例,别且生成一个xml格式的result文件保存在当前目录下

  7. pytest test_clac.py::TestClac::test_jia-----执行test_clac.py下的TestClac类中的test_jia测试用例

五.数据驱动

(1)测试数据的参数化

# filename:test_data.py
import pytest

# 方式一:使用string传参
class TestData1():
    @pytest.mark.parametrize("a,b",[(10,20),(30,40),(50,60)])
    def test_data1(self,a,b):
        print(a + b)

# 方式二:使用元组传参
class TestData2():
    @pytest.mark.parametrize(("a","b"),[(10,20),(30,40),(50,60)])
    def test_data2(self,a,b):
        print(a + b)

# 方式三:使用列表传参
class TestData3():
    @pytest.mark.parametrize(["a","b"],[(10,20),(30,40),(50,60)])
    def test_data3(self,a,b):
        print(a + b)

if __name__ == "__main__":
    pytest.main(['-v','-s','test_data.py'])

在这里插入图片描述

结合yaml给用例传递数据

方法:

1.将数据写在yaml文件中

2.在测试用例代码中使用yaml

例子:

# filename:data.yaml
-
  - 10
  - 20
-
  - 30
  - 40
-
  - 50
  - 60
# filename:test_data.py
import pytest
import yaml

# 方式一:使用string传参
class TestData1():
    @pytest.mark.parametrize("a,b",yaml.safe_load(open('./data.yaml')))
    def test_data1(self,a,b):
        print(a + b)

# 方式二:使用元组传参
class TestData2():
    @pytest.mark.parametrize(("a","b"),yaml.safe_load(open('./data.yaml')))
    def test_data2(self,a,b):
        print(a + b)

# 方式三:使用列表传参
class TestData3():
    @pytest.mark.parametrize(["a","b"],yaml.safe_load(open('./data.yaml')))
    def test_data3(self,a,b):
        print(a + b)

if __name__ == "__main__":
    pytest.main(['-v','-s','test_data.py'])

读取文件数据方法封装:

我们获取yaml数据的步骤可以抽离出来,封装为一个独立的方法

def get_data_from_yaml(path):
    with open(path,'r') as f:
        data = yaml.safe_load(f)
        return data

class TestCal:
    data = get_data_from_yaml
    
    @pytest.mark.parametrize('a,b',data(path='./case_data/test_calculatorcase_data.yaml'),ids=['first_case', 'two_case', 'three_case'])
    def test_add(self,a,b):
        assert True
        
# ids表示给用例取别名,每一组数据对应一条用例,对应一个别名

(2)测试步骤的参数化

可以将测试步骤也以数据的形式传给用例

# 测试代码文件 clac.py
class Clac:
    """创建加减乘除类"""

    def __init__(self,a,b):
        """获取数据"""
        self.a = a
        self.b = b

    def clac_jia(self):
        """加法计算方法"""
        return self.a + self.b

    def clac_jian(self):
    	"""减法计算方法"""
        return self.a - self.b

    def clac_cheng(self):
    	"""乘法计算方法"""
        return self.a * self.b

    def clac_chu(self):
    	"""除法计算方法"""
        return self.a / self.b
# 测试代码
import pytest
from python_yuan.clac import Clac #导入待测试的代码

@pytest.mark.parametrize('step',['jia','jian','cheng','chu'])
def test_step(step):
    a = 10
    b = 20 #实际工作中,测试数据也是参数化的。这里为了方便理解,把数据写死了。
    if step == 'jia':
        result = Clac(a,b).clac_jia()
        except1 = a + b
    elif step == 'jian':
        result = Clac(a,b).clac_jian()
        except1 = a - b
    elif step == 'cheng':
        result = Clac(a,b).clac_cheng()
        except1 = a * b
    elif step == 'chu':
        result = Clac(a,b).clac_chu()
        except1 = a / b
    assert result == except1

六.pytest fixture

1.自带的fixture(setup和teardown)

pytest中有自带的fixture方法,可以实现在测试用例前后完成特定的工作。

使用格式为:

setup_作用域:在测试用例执行前会执行setup中的内容
teardown_作用域:在测试用例执行后会执行teardown中的内容

常见的作用域如下:

  • function 定义在类外, 类外的每一个用例都执行
  • class 定义在类中,执行类中用例的前后会执行一次
  • module 定义在类外,执行模块中用例的前后会执行一次
  • session 定义在类外 每个session只运行一次,在自动化测试时,登录步骤可以使用session
  • method 定义在类中,类中的每个用例执行的前后会执行一次
  • setup/teardown 定义在类外,则类外的每个用例都执行;定义在类中,则类中的每个用例都执行

用例的执行顺序:

session级别 >module级别 >class级别>function级别。
例子:

import pytest #导入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_a():
    """以test开头的测试函数"""
    print("----->test_a")
    assert 1 #断言成功

def test_b():
    print('----->test_b')
    assert 'e' in 'et'

class Test_hh:
    def setup_class(self):
        """在整个类中用例测试开始前执行"""
        print("----->setup_class")

    def teardown_class(self):
        """在整个类中用例测试结束后执行"""
        print("----->teardown_class")

    def setup_method(self):
        """在每一个类中的用例测试开始前执行"""
        print("----->setup_method")

    def teardown_method(self):
        """在每一个类中的用例测试结束后执行"""
        print("----->teardown_method")

    def test_c(self):
        print('----->test_c')
        assert 'e' in 'et'

    def test_d(self):
        print('----->test_d')
        assert 'e' in 'et'


if __name__ == "__main__":
    pytest.main(["-s",'-v',"test_abc.py"])

效果如下:
在这里插入图片描述

2.自定义fixture

场景:
在这里插入图片描述
前面讲了setup、teardown可以实现在执行用例前或结束后加入一些操作,但这种都是针对整个脚本全局生效的。
如果有以下场景:用例 1 需要先登录,用例 2 不需要登录,用例 3 需要先登录。很显然无法用 setup 和 teardown 来实现。fixture可以让我们自定义测试用例的前置条件,并且可以跨文件使用。

  • 命名方式灵活,不局限于 setup 和teardown 这几个命名
  • conftest.py 配置里可以实现数据共享,不需要 import 就能自动找到fixture
  • scope=“session” 以实现多个.py 跨文件共享

实现自定义setup

# filename:test_login.py
import pytest #导入pytest

@pytest.fixture()
def login():
    """
    给对应的方法添加装饰器@pytest.fixture(),
    然后在需要调用的测试用例方法中传入该方法,即可。
    """
    print("登录方法")

def test_a(login):
    print('需要登录的测试用例')
    assert 1

def test_b():
    print('不需要登录的测试用例')
    assert 1

def test_c(login):
    print('需要登录的测试用例')
    assert 1

if __name__ == "__main__":
    pytest.main(['-v','-s','test_login.py'])

效果如下:
在这里插入图片描述

实现自定义teardown

在前面我们定义的fixture方法,只能实现在每个用例开始执行前执行(setup),那么怎么实现每个用例执行完成后执行呢(teardown)

方法:在定义的fixture方法中,可以使用yield进行区分,在yield前面的操作是setup,在yield后面的操作是teardown

import pytest #导入pytest

@pytest.fixture()
def login():
    """
    给对应的方法添加装饰器@pytest.fixture(),
    然后在需要调用的测试用例方法中传入该方法,即可。
    """
    print("登录方法") #setup
    yield

    print('退出登录方法') #teardown


def test_a(login):
    print('需要登录的测试用例')
    assert 1

def test_b():
    print('不需要登录的测试用例')
    assert 1

def test_c(login):
    print('需要登录的测试用例')
    assert 1

if __name__ == "__main__":
    pytest.main(['-v','-s','test_login.py'])

效果如下:
在这里插入图片描述

自定义fixture的作用域

使用@pytest.fixture(scope=‘module’)来定义作用域,scope的参数有以下几种

  • function 每一个用例都执行,不论是否在类中
  • class 每个类执行
  • module 每个模块执行(函数形式的用例)
  • session 每个session只运行一次,在自动化测试时,登录步骤可以使用该session
import pytest #导入pytest

@pytest.fixture(scope='class')
def login():
    """
    给对应的方法添加装饰器@pytest.fixture(),
    然后在需要调用的测试用例方法中传入该方法,即可。
    """
    print("登录方法") #setup
    yield

    print('退出登录方法') #teardown

class Test_login():

    def test_a(self,login):
        print('test_a')
        assert 1

    def test_b(self,login):
        print('test_b')
        assert 1

    def test_c(self,login):
        print('test_c')
        assert 1

if __name__ == "__main__":
    pytest.main(['-v','-s','test_login.py'])

效果如下:
在这里插入图片描述

通过conftest.py文件实现共享fixture

放在conftest.py文件下的fixture可以供整个conftest.py作用域下的用例文件使用,而不需要导入

conftest.py这个文件名是固定的,不可以更改。
使用的时候不需要导入conftest.py,会自动寻找.

演练:

第一步:创建一个conftest.py文件

# filename = conftest.py
#coding=utf-8
import pytest
@pytest.fixture()
def loginss():
    print('开始ss执行用例')
    yield
    print('执行ss用例结束')

第二步:在用例中使用fixture:loginss

import pytest
def test_a(loginss):
     assert True
def test_b(loginss):
     assert True
def test_c():
     assert True

注意conftest.py文件的作用域:

1.conftest.py创建在项目目录下,则项目下的所有用例文件都可共享

2.conftest.py创建在某个带__init__.py文件的包下,则该包下的所有用例文件都可共享

fixture中使用参数

在fixture中使用request.param来获取传进来的参数

import pytest
@pytest.fixture(params=["参数1","参数2"])
def myfixture(request):
    print("执行testPytest里的前置函数,%s" % request.param)

fixture中返回参数

import pytest
@pytest.fixture(params=["参数1","参数2"])
def myfixture(request):
    return request.param

def test_print_param(myfixture):
    print("执行test_two")
    print(myfixture)
    assert 1==1

输出:

PASSED                    [ 50%]执行test_two 
参数1
PASSED                    [100%]执行test_two
参数2

注意:

  • 对fixture参数化,也会使用例循环执行
  • 如果是setup中返回参数,使用yield代替return
  • 在用例中使用fixture函数名来获取fixture中返回的参数

七.pytest其他常用用法:

(1)pytest.mark.标记名

可以使用‘pytest.mark.标记名’来给用例添加标记

执行用例时使用’pytest -m 标记名‘来实现执行特殊标记的用例

import pytest

@pytest.mark.demo
@pytest.mark.smoke
def test_a():
     assert True

@pytest.mark.demo
def test_b():
     assert True

@pytest.mark.smoke
def test_c():
     assert True

def test_d():
     assert True

执行用例:

pytest -vs test_code.py -m="demo" # 执行模块下含demo标记的所有用例 ,执行test_a,test_b
pytest -vs test_code.py -m="demo and smoke" # 执行模块下含demo和smoke标记的所有用例 ,执行test_a
pytest -vs test_code.py -m "demo or smoke" # 执行模块下含demo或smoke标记的所有用例 ,执行test_a,test_b,test_c

注意:执行时,会报warning错误,可以在项目目录下创建一个pytest.ini文件,然后在pytest.ini文件中配置标记名,来解决该问题
在这里插入图片描述

(2)pytest.mark.skip直接跳过用例的执行

我们可以给用例加上装饰器pytest.mark.skip,来跳过该用例的执行。pytest会收集该用例,但不会执行

@pytest.mark.skip(reason='不想执行呢') # reason参数可以写跳过的原因,非必须参数
def test_a():
     assert True

def test_b():
     assert True

def test_c():
     assert True

def test_d():
     assert True

执行结果:

test_code.py::test_a SKIPPED
test_code.py::test_b PASSED
test_code.py::test_c PASSED
test_code.py::test_d PASSED

(3)pytest.mark.skipif()满足条件时,跳过用例

import pytest

@pytest.mark.skipif(1 == 1,reason='不想执行呢') # 满足条件时,跳过用例的执行
def test_a():
     assert True

def test_b():
     assert True

def test_c():
     assert Python测试框架pytest(23)插件 - pytest-pickedpytest-lazy-fixture

python WEB UI自动化在日期框中动态输入当前日期

Python+selenium+eclipse执行web自动化控件处理

Python+selenium+eclipse执行web自动化特殊控件--进度条

技术分享 | web自动化测试-文件上传与弹框处理

pytest接口自动化测试框架 | 汇总