pytest

一、pytest实践补充
1、测试报告report.html,要展示具体的log日志,不要在运行的时候加-vs

2、assert False ,str(‘mes’) 后面会输出mes的信息

3、assert True ,str(‘mes’) 就不会有任何输出

4、在assert前面添加print 就会有print内容输出在测试报告中

5、考虑如何不将所有的log日志打印到html报告里???

6、要使得传参成立 用pytest test_* -a= 1 -b=2 来执行有效

7、禁止掉失败用例的所有输出(stdout,stderr以及logs):
pytest --show-capture=no

一、pytest单元测试框架

1、单元测试:在软件开发郭恒忠,针对软件的最小单元(函数,方法)进行正确的检查测试

2、python: unittest,pytest

3、主要做什么:

1)测试发现:多个py文件中找到测试用例

2)测试执行:按照一定的顺序和规则执行,生成结果

3)测试判断:通过断言判断预期结果和实际结果

4)测试报告:统计测试进度,耗时,通过率,生成测试报告

二、单元测试框架和自动化测试框架关系?

1)自动化框架

1、提高我们测试效率,降低维护 成本

2、减少人工干预,提高测试的准确性,增加代码的重用性

3、核心思想:让不懂代码的人也可以通过这个框架实现自动化测试

单元测试框架是自动化测试框架的组成部分之一

ps: pom 数据驱动 关键字驱动 全局配置文件的封装 日志监控 selenium,request二次封装 断言 报告邮件

三、pytest简介

1、非常成熟的python的单元框架,比unittest更加灵活,容易上手

2、可以和selenium,requests,appium结合实现web自动化,接口自动化,app自动化

3、实现测试用例的跳过以及reuns失败用例重试

4、可以和allure生成非常美观的测试报告

5、和jenkins持续集成

6、pytest有很多非常强大的插件,并且能够实现很多的实用的操作

pytest   

pytest-html(生成html格式的测试报告)   

pytest-xdist(分布式执行,多cpu分发)

pytest-ordering(用于改变测试用例的默认执行顺序,默认从上到下)

pytest-rerunfailures 用例失败后重跑

allure-pytest 用于生成美观的测试报告

放到requirements.txt中, 终端进行pip install -r requirements

四、使用pytest,默认的测试用例的规则以及基础应用

1、模块名必须以 test_ or _test开头 或者结尾

2、测试类必须以Test开头,并且不能有init方法

3、测试方法必须以test开头

五、pytest测试用例的运行

1、主函数模式

if __name__ == "__main__":
   1) 运行所有 pytest.main()
   2)运行指定模块   pytest.main(['-vs','test_login.py'])

3) 指定目录

pytest.main(['-s','./interface_testcase'])
  1. 通过nodeid指定用例执行:nodeid通过模块名,分隔符,类名,方法名,函数名组成
pytest.main(['-vs','./interface_testcase/test_interface.py::test_04_func'])  函数
pytest.main(['-vs','./interface_testcase/test_interface.py::Testinterface::test_03_zhiliao'])  类下面的函数

2、命令行模式

1)运行所有 : pytest

2)指定模块: pytest -vs test_login.py

3)指定目录: pytest -vs ./interface_testcase

参数详解:

-s: 表示输出用例调试信息,以及print打印信息

-v:   显示更详细的信息

-vs: 这两个参数一起用

-n :支持多线程或者分布式运行测试用例               pytest.main(['-s','./interface_testcase','-n=2'])

-reruns num: 失败用例重跑                                      pytest.main(['-s','./interface_testcase','-n=2','--reruns=2'])

-x  表示有一个用例报错,测试停止、

--maxfail=2 出现两个用例失败,就停止

-k : 根据测试用例的部分字符串指定测试用例        pytest -vs ./testcase -k "ao" 包含ao的用例

--html 生成html的测试报告:  addopts = -vs --html ./report/report.html 

pytest -vs ./testcase -n 2

3、读取pytest配置文件运行

1、位置:pytest.ini 是pytest核心的配置文件,一般放在项目的根目录

2、编码格式:必须是ANSI 可以使用notepad++修改格式

3、作用:改变pytest默认的行为(默认是模块名必须以test开头)

4、运行的规则: 不管使用的是主函数还是命令行,都会去读取配置文件

[pytest]

命令行参数,用空格分隔

addopts = -vs

配置测试搜索的模块文件路径

addopts = -vs
testpaths = ./testcase
python_files = aaa_*.py 
python_classes = Test*     # 类名的规则
python_functions = test_*  # 方法名的规则
markers =                  # 用例分组
    smoke: 冒烟用例
 usermanage: 用户管理模块
 productmanage: 商品管理模块

六、pytest执测试用例的顺序是怎样的?

unittest: ascII的大小来决定执行的顺序

pytest: 默认执行顺序是从上向下

改变默认执行顺序: 使用mark标记

@pytest.mark.run(order=3) 这个标记决定执行顺序

七、如何分组执行(冒烟,分模块执行,分接口和web执行)

smoke: 冒烟用例,分布在各个模块里面
用例前加: @pytest.mark.smoke

终端执行:pytest -vs -m smoke

既执行smoke又执行usermanage:

pytest -vs -m " smoke or  usermanage"

八、pytest跳过用例

1、无条件

@pytest.mark.skip(reason="微微太漂亮")

2、有条件

@pytest.mark.skipif(age>=18,reason='已成年')

pytest框架实现的一些前后置(固件,夹具)的处理,常见有三种

九、setup/teardown, setup_class/teardown_class全局

为什么需要这些功能

比如:web自动化执行用例之前,需要打开浏览器吗,执行完毕需要关闭吗?

setup/teardown 在每个用例前后执行一次、

class Testmashang:

    def setup(self):
        print('\n在执行测试用例之前初始化代码:打开浏览器,加载网页')

    def test_09_baili(self):
        # time.sleep(3)
        print('\n测试百里')

    def test_08_xingyao(self):
        # time.sleep(3)
        print('\n测试星耀')

    def teardown(self):
        print('\n在执行测试用例之后扫尾的代码:关闭浏览器')
setup_class/teardown_class                        在每个类前后执行一次、

class Testmashang:

    # 这个方法在所有用例之前只执行一次
 def setup_class(self):
         print('\n在每个类之前初始化代码:创建日志对象,创建数据请求对象,创建接口的连接')

    # 在每个用例之前执行一次
 def setup(self):
        print('\n在执行测试用例之前初始化代码:打开浏览器,加载网页')

    def test_09_baili(self):
        # time.sleep(3)
        print('\n测试百里')

    def test_08_xingyao(self):
        # time.sleep(3)
        print('\n测试星耀')

    def teardown(self):
        print('\n在执行测试用例之后扫尾的代码:关闭浏览器')

    def teardown_class(self):
        print('\n在每个类之后扫尾的代码:销毁连接')
和unittest不一样,全是小写

十、使用@pytest.fixture装饰器来实现部分用例的前后置

@pytest.fixture(scope="",params="",autouse="",ids="",name="")

1、scope表示被@pytest.fixture标记的方法的作用域,function, class, module, package/session

初级用法:

import pytest

@pytest.fixture(scope="function")
def my_fixture():
    print('这是前后置的方法,可以实现部分以及全部用例的前后置')
    yield
    print('这是后置的方法')

class Testmashang:
    def test_09_baili(self):
        # time.sleep(3)
        print('\n测试百里')

    def test_08_xingyao(self,my_fixture):
        # time.sleep(3)
        print('\n测试星耀')

加上autouse = True 就可以全部进行使用

@pytest.fixture(scope=“module”, autouse=True) 模块的话只执行一次
@pytest.fixture(scope=“class”, autouse=True) 类的话在每个类执行一次

fixture优势

命名方式灵活,不局限于 setup 和teardown 这几个命名
1、conftest.py 配置里可以实现数据共享,不需要 import 就能自动找到fixture
2、scope=“module” 可以实现多个.py 跨文件共享前置
3、scope=“session” 以实现多个.py 跨文件使用一个 session 来完成多个用例

测试用例如何调用fixture

一、调用一个

# 1、将fixture作为测试用例函数的输入参数
# 2、测试用例加上装饰器: @pytest.mark.userfixtures(fixture_name)
# 3、fixture设置autouse=True 默认的是False

import pytest

@pytest.fixture
def login():
    print('先输入账号密码,再登录')

def test_01(login):
    print('登录后,进行操作01')

def test_02():
    print('不需要前面登录,直接操作02')
    
if __name__ == "__main__":
    pytest.main(['-vs'])

输出结果:

test_fixture.py::test_01 先输入账号密码,再登录
登录后,进行操作01
PASSED

test_fixture.py::test_02 不需要前面登录,直接操作02
PASSED

二、调用多个

import pytest

@pytest.fixture
def login():
    print('先输入账号密码,再登录')

@pytest.fixture
def login2():
    print('输入账号密码再登陆')

@pytest.mark.usefixtures('login2', 'login')
def test_03():
    print('用例03,账号密码登录后进行其他操作')


if __name__ == "__main__":
    pytest.main(['-vs'])

输出结果

test_fixture.py::test_03 输入账号密码再登陆
先输入账号密码,再登录
用例03,账号密码登录后进行其他操作
PASSED

三、自动调用

# 1、将fixture作为测试用例函数的输入参数
# 2、测试用例加上装饰器: @pytest.mark.userfixtures(fixture_name)
# 3、fixture设置autouse=True 默认的是False

import pytest

@pytest.fixture
def login():
    print('先输入账号密码,再登录')

@pytest.fixture
def login2():
    print('输入账号密码再登陆')

@pytest.fixture(autouse=True)
def login():
    print('自动调用')

@pytest.mark.usefixtures('login2', 'login')
def test_03():
    print('用例03,账号密码登录后进行其他操作')

if __name__ == "__main__":
    pytest.main(['-vs'])

输出结果:

test_fixture.py::test_03 自动调用
输入账号密码再登陆
用例03,账号密码登录后进行其他操作
PASSED

fixture实例化顺序

import pytest
order = []

# session > module > class > function
# s1 在 m1之前,
@pytest.fixture(scope='session')
def s1():
    order.append('s1')

@pytest.fixture(scope='module')
def m1():
    order.append('m1')

# f3在a1和f1之前
@pytest.fixture
def f1(f3, a1):
    # 先实例化f3,再实例化a1, 最后实例化f1
    order.append('f1')
    assert f3 == 123

@pytest.fixture
def f3():
    order.append('f3')
    a = 123
    yield a

@pytest.fixture
def a1():
    order.append('a1')

@pytest.fixture
def f2():
    order.append('f2')

def test_order(f1, m1, f2, s1):
    assert order == ['s1', 'm1', 'f3', 'a1', 'f1', 'f2']

if __name__ == "__main__":
    pytest.main(['-vs'])

输出结果:

test_fixtureorder.py::test_order PASSED

添加了 @pytest.fixture ,如果fixture还想依赖其他fixture,需要用函数传参的方式,不能用 @pytest.mark.usefixtures() 的方式,否则会不生效

@pytest.fixture(scope="session")
def open():
     print("===打开浏览器===")

@pytest.fixture
# @pytest.mark.usefixtures("open") 不可取!!!不生效!!!
def login(open):
 # 方法级别前置操作setup
     print(f"输入账号,密码先登录{open}")

前面讲的,其实都是setup的操作,那么现在就来讲下teardown是怎么实现的
用fixture实现teardown并不是一个独立的函数,而是用 yield 关键字来开启teardown操作

import pytest

@pytest.fixture(scope="function")
def my_fixture():
    print('这是前后置的方法,可以实现部分以及全部用例的前后置')
    yield
    print('这是后置的方法')


def test_08_xingyao(my_fixture):
    # time.sleep(3)
    print('\n测试星耀')

if __name__ == "__main__":
    pytest.main(['-vs'])

输出结果:

test_fixtureorder.py::test_08_xingyao 这是前后置的方法,可以实现部分以及全部用例的前后置
测试星耀
PASSED这是后置的方法

如果yield前面的代码,即setup部分已经抛出异常了,则不会执行yield后面的teardown内容
如果测试用例抛出异常,yield后面的teardown内容还是会正常执行

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pytest

@pytest.fixture(scope="session")
def open():
    print("===打开浏览器===") # 会话前置操作setup
    test = "测试变量是否返回"
    yield test
    print("==关闭浏览器==") # 会话后置操作teardown

@pytest.fixture
def login(open):
    print(f"输入账号,密码先登录{open}") # 方法级别前置操作setup
    name = "==我是账号=="
    pwd = "==我是密码=="
    age = "==我是年龄=="
    yield name, pwd, age # 返回变量
    print("登录成功") # 方法级别后置操作teardown

def test_s1(login):
    print("==用例1==") # 返回的是一个元组
    print(login) # 分别赋值给不同变量
    name, pwd, age = login
    print(name, pwd, age)
    assert "账号" in name # 断言
    assert "密码" in pwd
    assert "年龄" in age

def test_s2(login):
    print("==用例2==")
    print(login)

if __name__ == "__main__":
    pytest.main(['-vs'])

输出结果:

test_fixtureorder.py::test_s1 ===打开浏览器===
输入账号,密码先登录测试变量是否返回
==用例1==
('==我是账号==', '==我是密码==', '==我是年龄==')
==我是账号== ==我是密码== ==我是年龄==
PASSED登录成功

test_fixtureorder.py::test_s2 输入账号,密码先登录测试变量是否返回
==用例2==
('==我是账号==', '==我是密码==', '==我是年龄==')
PASSED登录成功
==关闭浏览器==

2、params: 作参数化 (支持list, tuple, 列表,元组,字典列表,字典元组{[],[],[]}

传参

@pytest.fixture(scope="function", params=['成龙', '甄子丹', '菜菜'])
def my_fixture(request):
    return request.param
params=['成龙', '甄子丹', '菜菜'] 是参数名
request.param 是属性名,是没有s的
@pytest.fixture(scope="function", params=['成龙', '甄子丹', '菜菜'])
def my_fixture(request):
    print('前置')
    yield request.param  # return和yield都表示返回,但是return的后面不能有代码,yield返回后可以接代码,return在yield后不能返回值?
    print('后置')
# 一个参数一个值
@pytest.mark.parametrize("input", ["输入值"])
def test_case1(input):
    print("\n" + input)
    assert input == "输入值"

if __name__ == "__main__":
   pytest.main(['-vs'])

输出结果:

test_fixtureorder.py::test_case1[\u8f93\u5165\u503c] 
输入值
PASSED

3、autouse= True 自动执行,默认是False

4、ids: 当使用params参数化是,给每一个值设置一个变量名,意义不大

@pytest.fixture(scope="function", params=['成龙', '甄子丹', '菜菜'], ids=['cl', 'zzd', 'cyl'])
输出后有ids

@pytest.mark.parametrize("input", ["输入值1", "输入值2"], ids=['cl', 'zzd'])
def test_case2(input):
    print("\n" + input)
    assert '输入值' in input

if __name__ == "__main__":
   pytest.main(['-vs'])

输出结果:

test_fixtureorder.py::test_case2[cl] 
输入值1
PASSED
test_fixtureorder.py::test_case2[zzd] 
输入值2
PASSED

多参数的混合使用

data1 = [1, 2]
data2 = ["python", "java"]
data3 = ["软", "件"]


@pytest.mark.parametrize("a", data1)
@pytest.mark.parametrize("b", data2)
@pytest.mark.parametrize("c", data3)
def test_case3(a, b, c):
    print(f"生成新的数据组合为:[{a} {b} {c}]")

if __name__ == "__main__":
   pytest.main(['-vs'])

输出结果


test_fixtureorder.py::test_case3[\u8f6f-python-1] 生成新的数据组合为:[1 python 软]
PASSED
test_fixtureorder.py::test_case3[\u8f6f-python-2] 生成新的数据组合为:[2 python 软]
PASSED
test_fixtureorder.py::test_case3[\u8f6f-java-1] 生成新的数据组合为:[1 java 软]
PASSED
test_fixtureorder.py::test_case3[\u8f6f-java-2] 生成新的数据组合为:[2 java 软]
PASSED
test_fixtureorder.py::test_case3[\u4ef6-python-1] 生成新的数据组合为:[1 python 件]
PASSED
test_fixtureorder.py::test_case3[\u4ef6-python-2] 生成新的数据组合为:[2 python 件]
PASSED
test_fixtureorder.py::test_case3[\u4ef6-java-1] 生成新的数据组合为:[1 java 件]
PASSED
test_fixtureorder.py::test_case3[\u4ef6-java-2] 生成新的数据组合为:[2 java 件]
PASSED

============================== 8 passed in 0.34s ==============================

Process finished with exit code 0

参考

5、name: 表示是被@pytest.fixture标记的方法取一个别名

给标记的方法起一个别名,使用别名,再用本身的方法来传参,是不能用的

十一、通过conftest.py和@pytest.fixture结合使用实现全局前置应用(比如:项目的全局登录,模块的全局处理)

1、conftest.py 文件是单独存放的一个夹具配置文件,名称是不能更改的

2、可以再不同的py文件中使用同一个fixture函数(跨文件使用前置)

3、contfest.py 文件需要和运行的用例放在同一层,并且不需要任何import导入的操作(原则上同层,但是不同层也一样可以做到)

总结:

setup/teardown ,setup_class/teardown_class 作用于所有用例或者所有的类

@pytest.fixtue() 它的作用既可以部分也可以全部前后置

conftest.py和@pytest.fixture()结合使用,作用于全局的前后置

@pytest.fixture(scope="function")
def all_fixture():
    print('全局前置')
    yield
    print('全局后置')


@pytest.fixture(scope="function")
def user_fixture():
    print('用户管理前置')
    yield
    print('用户管理后置')
class Testmashang1:

    def test_userall_xingyao(self, all_fixture, user_fixture):
        # time.sleep(3)
        print('\n测试星耀')
        print('--------------------'+str(user_fixture)) # 先局部变量
        print(all_fixture)                              # 再全局变量

十二、断言

assert

assert 1==2

十三、pytest结合allure-pytest插件生成allure测试报告

allure-pytest

1、下载,解压,配置path路径

参考文件:

如果dos可以验证,pycharm验证不了,重启pycharm

2、加入命令生成json格式的临时报告
pytest.ini加入
addopts = -vs --alluredir ./temp
3、生成allure报告
在pytest.main() 后面构建
os.system('allure generate ./temp -o ./report --clean') # 固定语法+找到临时报告+输出+输出到当前report目录下+清空原有的报告
生成allure的json文件:

pytest test_03_neiwanglogin.py --loopcount=1 --checkloopcount=2 --alluredir ./tmp

由json文件生成allure报告:

allure generate ./tmp/ -o ./tmpreport/ --clean

优点: 生成的json文件,可以直接看到log日志,以及输出结果等, 并且报告中也分别独立展示

二、自带报告

pytest test_03_neiwanglogin.py --html=report.html --loopcount=1 --checkloopcount=2  # 如果出现-s就禁止capture输出

优点:所有内容都可以在一个页面中展示

缺点:会显示log日志,目前没有办法只禁止log日志的输出,循环次数较多的时候,报告内容过长

三、选择allure原因
由于html报告展示了全部日志

目前没有找到让部分日志不显示的方法,所以用allure测试报告,会有分开的json文件,以及报告里面各个显示情况是独立的,可以做参考

十四、@pytest.mark.parametrize()基本用法

@pytest.mark.parametrize(args_name, args_value)

args_name: 参数名

args_value: 参数值(列表,元组,字典列表,字典元组)有多少个值用例就会执行多少次

第一种方式:

import pytest

class Testapi:

     @pytest.mark.parametrize('args', ['百里', '星耀', '依然'])
     def test_01_baili(self, args):
         print(args)


if __name__ == "__main__":
    pytest.main()

输出结果:

test_fixtureorder.py::Testapi::test_01_baili[\u767e\u91cc] 百里
PASSED
test_fixtureorder.py::Testapi::test_01_baili[\u661f\u8000] 星耀
PASSED
test_fixtureorder.py::Testapi::test_01_baili[\u4f9d\u7136] 依然
PASSED

第二种方式:跟unittest的ddt里面的@ynpack解包一样

import pytest
class Testapi:

     @pytest.mark.parametrize('name, age',[['百里', '38'], ['星耀', '18']])
     def test_01_baili(self, name, age):
         print(name, age)

if __name__ == "__main__":
    pytest.main()

输出结果:

test_fixtureorder.py::Testapi::test_01_baili[\u767e\u91cc-38] 百里 38
PASSED
test_fixtureorder.py::Testapi::test_01_baili[\u661f\u8000-18] 星耀 18
PASSED

十五、YAML文件详解—实现接口自动化

1、用于全局的配置文件 ini yaml

2、用于写接口的测试用例

yaml简介:

yaml是一种数据格式, 支持注释,换行,多行字符串,裸字符串(整形,字符串)

语法规则:

1、区分大小写

2、使用缩进表示层级,不能使用tab键,只能用空格键

3、缩进没有数量的,只要前面是对其就行

4、注释是#

数据组成:

1、Map对象, 键值对 键:(空格)值

多行写法

msxy:

name: 百里

age: 18

一行写法

msxy: {name: 百里, age: 18}
检测yaml是否是正确格式的网站: https://www.bejson.com/validators/yaml_editor/

2、数组(list) 用一组横线开头

msxy:

  • name: 百里
  • age: 18
    msxy: [{name: 百里}, {age: 18}]

2、断言的封装

3、allure报告的定制

4、关键字驱动和数据驱动结合实现接口自动化测试

5、python的反射

正常:先初始化对象吗,再调方法

反射:通过对象得到类对象,然后通过类对象使用方法

6、jenkins的持续集成和allure的报告继承,并且根据自动化的报告的错误率去发送电子邮件

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值