UI自动化测试-pytest框架

 🔥 交流讨论:欢迎加入我们一起学习!

🔥 资源分享耗时200+小时精选的「软件测试」资料包

🔥 教程推荐:火遍全网的《软件测试》教程  

📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!

在进行UI自动化测试的时候,我们需要工具来对测试用例进行收集,执行,标记,参数化。pytest就是这样一个工具。

pytest实际是python的一个单元测试框架,其他还有如unittest等,它可以实现按照规则搜索测试用例,对测试用例进行标记,如只执行L1

级别的测试用例,测试执行用例失败后重新执行,测试用例的参数化等。

pytest不仅适用于UI自动化测试,也适用于接口自动化,对python语言进行单元测试等。

安装pytest

可以使用pip或者pycharm方式进行安装

打开pycharm,左上角File-settings-project:项目名-python interpreter,点击+,搜索pytest,然后install package

第一个pytest脚本

import pytest

class TestDemo:
    def test_01(self):
        assert 1==1  # 断言成功

    def test_02(self):
        assert 1==2  # 断言失败
if __name__ == '__main__':
    pytest.main(['-s','test.py'])

在文件输入如上代码。

在这个脚本中,我们首先导入了pytest模块,然后定义了一个class,并定义了两个方法,在方法里用assert进行断言。

assert是python自带的关键字。assert后面接一个表达式,只要表达式的最终结果为True,那么断言通过,用例执行成功,否则用例执行失败。可以看出,第一个方法里结果为true,第二个方法里结果为false。

在main主入口里,调用pytest的main方法,并传入模块名(即文件名)。

点击运行按钮,结果如下:

由结果可以看出,一共收集了两个用例,其中一个运行成功(用.表示),一个运行失败(F表示),失败的原因是1!=2

用例收集规则

pytest按照一定的规则进行用例的收集。

1. 模块名必须以test_开头或_test结尾

这里适用于未指定模块时的收集,如果运行单个文件,在main里指定要执行的文件,则无论这个文件名是什么,都会被执行。

如果在main里不指定模块,如只写pytest.main(),则pytest会默认从当前路径及其所有子目录中搜索py源文件,所有名字以test_开头或者以_test结尾的python源文件(.py文件)被认为是测试模块源文件,不符合这个命名规则的文件会被忽略。

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

一个文件也可以只写测试方法,不写测试类。

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

在上面的示例脚本中,我们定义了两个test_开头的方法,所以收集了两个用例。非test开头的方法,会被pytest忽略。

运行方式

main函数运行

我们可以在main入口中调用pytest的main方法来运行文件,如上例。

不指定模块名
pytest.main(['-s'])

-s参数表示在控制台输出信息,当不指定模块名时,可以看到,收集了7条用例

指定模块名
 pytest.main(['-s','test.py'])

当指定了模块后,运行文件,只收集当前文件的两条用例。

指定class
 pytest.main(["-s", "test.py::TestDemo"])

我们也可以在模块名上加上指定的class名

命令行窗口运行

不指定模块名

打开terminal,进入目标目录,直接执行“pytest”即可自动寻找当前目录下的测试用例

指定模块名

打开terminal,输入pytest+模块名,指定模块执行。

pytest test.py

指定函数名

打开terminal,输入pytest+模块名+函数名,指定模块执行。

pytest test.py::TestDemo

test runner运行

在File-Settings如图路径,设置default test runner为pytest,点击OK。

再回到测试文件,可以看到类,方法前面都显示运行按钮,点击对应按钮,可以运行类,单独运行某个test。

也可以在不写main函数的情况下,右键以pytest运行文件。注意在配置了runner后,执行不会进入main主入口。

标记机制

pytest提供了标记机制,借助“mark”关键字,我们可以对测试函数(类、方法)进行标记。

我们可以利用标记,对测试用例进行分级,例如某些主流程的用例可以标记为L1,次要流程的用例标记为L2等。这样有一个好处,我们可以在不同的情况执行不同的测试用例,例如,在做冒烟测试的时候,只需要执行L1级别的用例就行了。

●一个测试函数(类、方法)可以有多个标记。

●一个标记也可以应用于多个函数(类、方法)。

●执行参数使用:pytest -m mark名。

●执行多个标记:pytest -m “L1 or L2”。

import pytest

class TestDemo:

    @pytest.mark.L1
    @pytest.mark.L2
    def test_01(self):
        assert 1==1  # 断言成功

    @pytest.mark.L2
    def test_02(self):
        assert 1==2  # 断言失败
if __name__ == '__main__':
    pytest.main(['-m L2','test.py'])

如上,给测试用例添加标记,并在main中以-m参数指定选择的标记,也可以使用or和and。

  pytest.main(['-m L2 or L1','test.py']) # 执行标记为L1或者L2的
  pytest.main(['-m L2 and L1','test.py']) #执行标记同时为L1和L2的

测试固件

在进行UI测试时,可能每个用例的操作前提都是需要登录系统,那么我们也不能在每次写用例的时候,都把登录写一遍,或者调用一遍,这样会造成代码冗余。有没有可能,在整个测试类或者所有测试用例执行之前,调用一次登录呢?

这样的工具,就是测试固件,测试固件有不同级别。

函数中的测试固件

●setup_module、teardown_module,在整个文件的开始和最后执行一次。

●setup_function和teardown_function,在每个函数开始前后执行。


import pytest
 
'''
在函数中使用
1.setup_module、teardown_module,在整个文件的开始和最后执行一次
2.setup_function和teardown_function,在每个函数开始前后执行
'''
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():
     print('aaaa')
     assert 1 == 1
 
def test_b():
     print('bbbb')
     assert 1 == 2
 
if __name__ == '__main__':
     pytest.main(["-s", "test.py"])

class中的测试固件

●setup_class、teardown_class,在整个class的开始和最后执行一次。

●setup_method和teardown_method,在每个方法开始前后执行。

class TestDemo():
    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_a(self):
        print('aaaa')
        assert 1 == 1

    def test_b(self):
        print('bbbb')
        assert 1 == 2


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

setup和teardown

setup和teardown既可以应用在函数中,也可以应用在class中,作用对象是函数或方法,在每个测试用例执行前后执行一次。

我们通常使用setup来进行一些数据准备工作,使用teardown来进行数据清理工作。

import pytest

class TestDemo:
    def setup(self):
        print('setup')
    def teardown(self):
        print('teardown')
    @pytest.mark.L1
    @pytest.mark.L2
    def test_01(self):
        assert 1==1  # 断言成功

    @pytest.mark.L2
    def test_02(self):
        assert 1==2  # 断言失败
if __name__ == '__main__':
    pytest.main(['-s','test.py'])

断言

我们在进行手工测试时,测试用例有预期结果,那么在进行UI测试时,我们也需要来判定结果是否符合我们的预期。

在python中用断言来实现。断言assert用于检查指定的表达式的结果是否为True。

一个测试用例可以写多个断言,当有一个断言失败时,pytest就认为测试的结果为失败,该测试用例的执行会被终止。

如果一条测试用例不写断言,那么它的结果为通过。所以一定要写断言,否则测试执行就没有了意义。

import pytest

class TestDemo:
    
    @pytest.mark.L1
    @pytest.mark.L2
    def test_01(self):
        assert 1==1  # 断言成功
        assert 1==3

    @pytest.mark.L2
    def test_02(self):
        assert 1==2  # 断言失败
if __name__ == '__main__':
    pytest.main(['-s','test.py'])

参数化

在进行UI测试时,我们需要给测试用例传测试数据,那么如何做呢?

pytest使用@pytest.mark.parametrize装饰器来实现数据驱动测试,也就是常说的参数化。

单个参数

import pytest

class TestDemo:
    data = ["小红", "小明"]
    @pytest.mark.parametrize("username",data)
    def test_01(self,username):
        print("我是{}".format(username))


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

运行结果如下,这里给一条测试用例传入两条数据,显示收集到了两条用例。

多个参数

列表嵌套字典
import pytest

class TestDemo:

    data_1 = [
        {"username": "admin1", "password": "123456"},
        {"username": "admin2", "password": "12345678"},
    ]

    @pytest.mark.parametrize("data", data_1)
    def test_login(self,data):
        print("账号:{},密码:{}".format(data["username"], data["password"]))


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

可以看到这里传入两条数据,显示收集了两条用例。

列表嵌套列表
import pytest

class TestDemo:

    data_1 = [
        ["admin1", "123456"],
        ["admin2", "12345678"],
    ]

    @pytest.mark.parametrize("username,password", data_1)
    def test_login(self,username, password):
        print("账号:{},密码:{}".format(username, password))


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

列表嵌套元组
import pytest

class TestDemo:

    data_1 = [
        ("admin1", "123456"),
        ("admin2", "12345678"),
    ]
    @pytest.mark.parametrize("username,password", data_1)
    def test_login(self,username, password):
        print("账号:{},密码:{}".format(username, password))


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

本文介绍了pytest测试框架的安装、运行、用例收集、标记、测试固件、断言、参数化等内容,实际这些内容只是pytest的冰山一角,还有

失败重跑,测试报告,全局设置等内容没有进行介绍,但学习了本文的内容,相信你已经可以上手进行自动化测试用例的编写了。

欢迎交流,拜拜!

最后我邀请你进入我们的【软件测试学习交流群:785128166】, 大家可以一起探讨交流软件测试,共同学习软件测试技术、面试等软件测试方方面面,还会有免费直播课,收获更多测试技巧,我们一起进阶Python自动化测试/测试开发,走向高薪之路

作为一个软件测试的过来人,我想尽自己最大的努力,帮助每一个伙伴都能顺利找到工作。所以我整理了下面这份资源,现在免费分享给大家,有需要的小伙伴可以关注【公众号:程序员二黑】自提!

  • 25
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值