python 测试框架

单元测试

什么是单元测试

单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用来判断某个特定条件(或者场景)下某个特定函数的行为

单元测试越早介入越好

单元测试需要注意什么?

单元测试的时候一个大前提就是需要清除的知道,自己要测试的程序块所预期的输入和输出,然后根据这个预期和程序逻辑来书写case。这里的预期结果一定要针对需求/设计的逻辑去写,而不是针对程序的实现去写,否则单元测试就失去了意义,照着错误的实现设计出的case很可能也是错的。

单元测试框架

  • Unittest

    • python内置的标准类库。它的API与Java的JUnit、.net的NUnit,C++的CppUnit很相似
  • Pytest

    • 丰富、灵活的测试框架,语法简单,可以结合allure生成一个炫酷的测试报告,现在比较主流
  • Nose

    • Nose是对unittest的扩展,使得python的测试更加简单
  • Mock
    *unittest.mock是用来测试python的库。这个是一个标准库(出现在Python3.3版本以后)

单元测试覆盖率

也叫代码覆盖率,也被用于自动化测试和手工测试来度量测试是否全面的指标之一,应用覆盖率的思想增强测试用例的设计
单元测试覆盖类型:

  • 语句覆盖:

    • 通过设计一定量的测试用例,保证被测试的方法每一行代码都会被执行一遍
    • 运行测试用例的时候被击中的代码行即称为被覆盖的语句
    • 对于例子中的程序,只需要一条case,即可实现行覆盖(a=3,b=0,x=3)
    • 漏洞:如果and写成or,仍能通过测试
  • 条件覆盖

    • 条件覆盖与判断覆盖类似,不过判定覆盖关注整个判定语句,而条件覆盖则关注某个判断条件
    • 测试用例: if(a>1 and b==0)
    • 缺陷:测试用例指数级增加(2**conditions)
  • 判断覆盖

    • 运行测试用例的过程被击中的判定语句
    • 测试用例
      图片说明
    • 漏洞
    • 大部分的判定语句是由多个逻辑条件组合而成,若仅仅判断每个条件的取值情况,必然会遗漏部分测试路径(a==2 or x>1 --------> a==2 or x<1)
  • 路径覆盖

    • 覆盖所有可能执行的路径
    • 测试用例(画一下流程图)
      图片说明

例子:

图片说明

ubbitest框架介绍

  • python自带的单元测试框架,常用在单元测试
  • 在自动化测试中提供用例组织与执行
  • 提供丰富的断言方法-验证函数等功能
  • 加上HTML TestRunner可以生成html的报告
  • 现在依然有一些公司在用这个框架

unittest

unitest编写与规范

Unittest提供了test cases、test suites、test fixtures、test runner相关的组件

  • test cases:测试用例
  • test suites:测试用例集合
  • test fixtures:执行测试用例之前和之后会进行一些清理工作
  • test runner:测试用例执行

编写规范

  • 测试模块首先import unittest
  • 测试类必须继承unittest.TestCase
  • 测试方法必须以“test_”开头
  • 模块名字,类名没有特殊要求

测试框架结构

import unittest
class demo(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:   #setUpClass和tearDownClass作用域是整个类,在整个类运行之前和之后分别执行
        print("setup class")
    @classmethod
    def tearDownClass(cls) -> None:
        print("teardown class")

    def setUp(self) -> None:     #setUp 和tearDown的作用域是测试用例,在每个测试用例运行之前和之后分别执行
        print("setup")

    def tearDown(self) -> None:  #回收资源
        print("teardown")

    def test_case01(self):
        print("testcase01")
        self.assertEqual(2,2,"判断相等")
        #self.assertIn('h','this')

    def test_case02(self):
        print("testcase02")
        self.assertEqual(1,2,"判断相等")
        #self.assertIn('h','this')

    # 跳过某个测试用例
    #@unittest.skip
    #也可以在满足某个条件时跳过:
    #@unittest.skip(1+1==2,"跳过这条用例")
    def test_case03(self):
        print("testcase03")
        self.assertEqual(4,4,"判断相等")
        #self.assertIn('h','this')


if __name__ == '__main__':
    unittest.main()

unittest断言

常用断言:
assertEqual
assertIn
assertTrue
基本的断言方法提供了测试结果是True还是False,所有的断言方法都有一个msg参数,如果指定msg参数的值,则将该信息作为失败的错误信息返回

图片说明

unittest执行测试用例

  • 方法一
    unittest.main():把当前模块下的测试用例(以test_开头的方法)全部执行

  • 方法二——加入容器中执行(需要在pycharm进行设置,把默认的unittest执行去除,添加要执行的文件)
    suite=unittest.TestSuite
    suite.addTest(TestMethod("test_01"))
    suite.addTest(TestMethod("test_02"))
    unittest.TextTestRunner().run(suite)
    图片说明
    图片说明

  • 方法三——此用法可以同时测试多个类
    suite1=unittest.TestLoader().loadTestsFromTestCase(TestCase1)
    suite2=unittest.TestLoader().loadTestsFromTestCase(TestCase2)
    suite=unittest.TestSuite([suite1,suite2])
    unittest.TextTestRunner(verbosity=2).run(suite)

  • 方法四——匹配某个目录下所有以test开头的py文件,执行这些文件下的所有测试用例

    • test_dir="./test_case"
    • discover=unittest..defaultTestLoader.discover(test_dir,pattern="test*.py")
      • discover可以一次调用多个脚本
      • test_dir被测试脚本的路径
      • pattern脚本名称匹配规则

测试用例执行过程

图片说明

unittest结合htmltestrunner生成带日志的测试报告

pytest测试框架

pytest介绍

  • pytest是一个非常成熟的全功能的Python测试框架
    • 简单灵活,容易上手
    • 支持参数化
    • 测试用例的skip和xfail,自动失败重试等处理
    • 能够支持简单的单元测试和复杂的功能测试,还可以用来做selenium/appnium等自动化测试、接口自动化测试(pytest+requests)
    • pytest具有很多第三方插件,并且可以自定义扩展,比较好用的如pytest-allure(完美html测试报告生成),pytest-xdist(多CPU分发)等
    • 可以很好的和jenkins集成
  • 文档:https://docs.pytest.org/en/latest/contents.html#toc
  • 第三方库:https://pypi.org/search/?q=pytest

pytest安装与依赖

  • pip install -U pytest:U表示升级
  • pip install pytest-sugar:对运行过程的界面进行美化
  • pip install pytest-rerunfailures:重新运行出错的测试用例
  • pip install pytest-xdist:多任务同时并发的执行测试用例
  • pip install pytest-assume:执行每一条断言
  • pip install pytest-html:生成测试报告
    ...
  • pip list:查看
  • pytest -h:帮助

测试用例的识别与运行

  • 测试文件
    • test_*.py
    • *_test.py
  • 用例识别
    • Test类包含的所有test_的方法(测试类中不能带有init方法)
    • 不在class中的所有test_*方法
  • pytest也可以执行unittest框架写的用例和方法
  • pytest执行
    图片说明
    可以在命令行中直接执行,输入pytest 文件名(要在包含该文件的目录下执行)
    图片说明
    常用参数:
    • -v:(最高级别信息--verbose)打印详细的日志信息(哪一个模块的哪一个方法执行成功/失败)
    • -s:s是带控制台输出结果,也是输出详细(可以将文件中print的信息打印到终端中)
    • pytest 文件名::类名:可以执行某一个类里的测试用例
    • pytest 文件名::类名::方法名:可以执行某个模块里面某个类里面的方法
    • pytest -v -k "类名 and not 方法名":跳过运行某个用例
    • pytest -m[标记名] :@pytest.mark.[标记名]将运行有这个标记的测试用例
    • pytest -x 文件名:一旦运行到报错就停止
    • pytest --maxfail=[num]:当运行错误达到num的时候就停止运行

pytest执行-失败重新运行

  • 场景:测试失败后要重新运行n次,要在重新运行之间添加延迟时间,间隔n秒再运行
  • 安装:pip install pytest-rerunfailures
  • 执行:
    • pytest --reruns 3 -v -s test_class.py(重复运行3次)
    • pytest -v --reruns 5 --reruns-delay 1(间隔1s)

pytest执行-多条断言有失败也都运行

  • 场景:一个方法中写多条断言,通常第一条过不去,下面就不执行了。但是我们想报错也都执行一下就可以采用这种方法
  • 安装:pip install pytest-assume
  • 执行:
    • pytest.assume(1==4)
    • pytest.assume(2==4)
    • pytest.assume('h' in 'this')
      (把每一条的信息都打印出来)

pycharm中配置与执行pytest测试框架

  • 运行方法:pytest.main(['-v','TestClass'])(所有参数和pytest命令行方式是一样的)
  • 配置:
    图片说明
  • 执行:
    图片说明

pytest框架结构

import pytest类似的setup,teardown同样更加灵活

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

例:

import 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_login():
    print("这是个外部的方法")

class TestDemo:
    def setup_class(self):
        print("这是个setup_class方法")

    def setup_method(self):
        print("这是个setup_method方法")

    def setup(self):
        print("这是个setup方法")

    def teardown_class(self):
        print("这是个teardown_class方法")

    def teardown_method(self):
        print("这是个teardown_method方法")

    def teardown(self):
        print("这是个teardown方法")

    def test_one(self):
        print("开始执行test_one方法")
        x = 'this'
        assert 'h' in x

    def test_two(self):
        print("开始执行test_two方法")
        x = 'hello'
        assert 'e' not in x

    def test_three(self):
        print("开始执行test_three方法")
        a = 'hello'
        b = 'hello world'
        assert a in b

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

结果:
图片说明
图片说明
图片说明
图片说明

pytest-fixture的用法

  • 场景:

    • 用例1需要先登录
    • 用例2不需要登录
    • 用例3需要登录
  • 这种场景无法用setup和teardown实现

  • 这时候可以通过pytest-fixture实现

  • 用法:在方法前面加@pytest.fixture()

例:前端自动化中的应用

  • 场景:测试用例执行时,有的用例需要登录才能执行,有些用例不需要登录。setup和teardown无法满足,fixture可以。默认scope(范围)function
  • 步骤:
    • 导入pyteat
    • 在登录的函数上面加@pytest.fixture()
    • 在要使用的测试方法中传入(登录函数名称),就先登录
    • 不传入的就不登录,直接执行测试方法
      import pytest
      @pytest.fixture()
      def login():
      print("这是个登录方法")
      def test_case1(login):
      print("test_case1,需要登录")
      pass
      def test_case2():
      print("test_case2,不需要登录")
      pass
      def test_case3(login):
      print("test_case3,需要登录")
      pass
      if __name__ == '__main__':
      pytest.main()
      执行结果:
      图片说明

例:前端自动化中应用-conftest

  • 场景:
    与其他测试工程师合作一起开发时,公共模块要在不同文件中,要在大家都访问到的地方
  • 解决:
    conftest.py这个文件进行数据共享,并且可以放在不同位置起着不同范围共享作用
  • 执行:
    系统执行到参数login时先从本文件中查找是否有这个名字的变量,之后再conftest.py中找是否有
  • 步骤:
    将登陆模块带@pytest.fixture写在conftest.py中
  • conftest.py配置需要注意:
    • conftest文件名不能换
    • conftest.py与运行的用例要写在同一个package下,并且有init/py文件
    • 不需要import导入conftest.py,pytest用例会自动查找
    • 全局的配置和前期工作都可以写在这里,放在某个包下,就是这个包数据共享的地方

例:前端自动化中应用——yield

  • 场景:使用fixture可以将测试方法前要执行的或依赖的解决了,测试方法后销毁清除数据的要如何进行呢?范围是模块级别的。类似setupClass
  • 解决:
    通过在同一模块中加入yield关键字,yield是调用第一次返回结果,第二次执行它下面的语句返回
  • 步骤:
    在方法前面加上pytest.fixture(scope=module)
  • 在登录的方法中加yield,之后加销毁清楚的步骤注意,这种方式没有返回值,如果希望返回使用addfinalizer

多线程并行与分布式执行

  • 场景:测试用例1000条,一个用例执行1分钟,1个测试人员执行需要1000分钟,通常我们会用人力成本换取时间成本,加几个人一起执行,时间就会缩短。如果10个人一起执行只需要100分钟,这就是一种并行测试,分布式场景
  • 解决:pytest分布式执行插件,多个CPU或主机执行。前提:用例之间都是独立的,没有先后顺序,随机都能执行,可重复运行不影响其他用例
  • 安装:
    • pip3 install pytest-xdist
    • 多个CPU并行执行用例,直接加-n 3是并行数量:pytest -n 3
    • 在多个终端下一起执行

pytest-html生成报告

  • 安装:
    pip install pytest-html
    生成html报告
    pytest -v -s --html=report.html--self-contained-html

pytest参数化

@pytest.mark.parametrize(argnames,argvalues)

  • argnames:要参数化的变量,string(逗号分隔),list,tuple
  • argvalues:参数化的值,list,list[tuple]

使用string

@pytest.mark.parametrize("a,b",[(10,20),(10,30)])
def test_param(self,a,b)
print(a+b)

使用list

使用tuple

yaml数据参数化

  • yaml实现list:-10 -20 -30
  • yaml实现字典:key:value
  • yaml进行嵌套
  • 加载yaml文件:
    • yaml.safe_load(open("./data.yaml"))
  • pytest与yaml连用:
    @pytest.mark.parametrize(["a","b"],yaml.safe_load(open("./data.yaml")))
    def test_param(self,a,b):
    print(a+b)
    (yaml内容是个二位数组)
全部评论

相关推荐

点赞 评论 收藏
分享
2024-12-27 10:21
已编辑
海南师范大学 媒介策划
到我怀里来:身高体重住址这些就别写了,留几个关键的就行,工作经历突出重点写详细点
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客企业服务