Unittest测试框架基础及进阶

发布时间 2023-10-10 20:52:23作者: 韩志超

需求

假设领导让你开发一个接口测试框架。领导提出了一些新的需求,你如何实现?

Unittest测试框架基础

Unittest测试框架介绍

Unittest是Python自带的测试框架,提供基础的用例管理和测试控制功能,使用灵活易于定制。Unittest中主要包含TestCase测试用例、TestSuite测试套件、测试准备及清理方法和TestRunner测试运行器等主要概念,另外还包含TestLoader用于批量加载用例生成测试套件,TestResult用于在TestRunner中记录测试结果。

Unittest测试框架使用

基础使用

编写用例
test_demo1.py
import requests

import unittest

class TestDemo(unittest.TestCase):
    def setUp(self):
        self.session = requests.session()

    def tearDown(self):
        self.session.close()

    def test_get(self):
        """测试Get接口"""
        url = 'https://postman-echo.com/get?a=1&b=2'
        res = self.session.get(url)
        self.assertDictEqual({'a': 1, 'b': 2}, res.json()['args'])

运行方法

$ python3 –m unittest test_demo1.py -vvv

自定义测试套件运行方法

loader = unittest.TestLoader()
suite = loader.discover('.testcases')
runner = unittest.TextTestRunner()
result = runner.run(suite)

测试用例的生命周期

  • setUp()方法在每个测试方法执行之前调用,用于准备测试环境。
  • tearDown()方法在每个测试方法执行之后调用,用于清理测试环境。
  • setUpClass()方法在测试类中的所有测试方法执行之前调用,用于准备测试环境。
  • tearDownClass()方法在测试类中的所有测试方法执行之后调用,用于清理测试环境。

测试用例的组织和管理

  • 可以使用TestSuite类来组织多个测试用例。
  • 可以使用TestLoader类来动态加载测试用例。
  • 可以使用TestResult类来收集测试结果和生成测试报告。

跳过用例及期望失败

待补充

ddt数据驱动

安装ddt

$ pip install ddt

使用方法

待补充

ddt与unittest subtests的区别

  • subtests: 一个测试用例,循环测试失败不中断测试
  • ddt: 生成多个测试用例

ddt数据驱动原理

  • @ddt.data(): 为测试函数添加数据属性
  • @ddt.ddt: 遍历测试类中所有带数据属性的测试函数,动态在类中添加添加多个测试函数,并赋予数据

Unittest测试框架原理

Unittest测试框架的的原理是将继承unittest.TestCase的测试类中,所有的test开头的测试函数,生成该测试类的一个对象,然后组装成测试套件,使用测试运行器(TestRunner)运行并使用测试结果(TestResult)对象纪录每个用例的运行状态。

import unittest

class TestDemo(unittest.TestCase):
    def test_1(): ...

    def test_2(): ...

    def test_3(): ...

# 运行方法
loader = unittest.TestLoader()
# 生成测试套件-将test_1,、test_2、 test_3生成TestDemo的对象,添加到测试套件中
suite = loader.loadTestsFromTestCase(TestDemo)
for test in suite:
    print(test.id())

runner = unittest.TextTestRunner()
result = runner.run(suite)

Unittest测试框架基本结构

  • TestCase :测试用例
  • TestSuite:测试套件
  • TestLoader:测试用例加载器
  • TestResult:测试结果记录器
  • TestRunner:测试运行器

image

TestCase类

常用方法

  • setUp():需用户实现,单条测试用例准备方法
  • tearDown():需用户实现,单条测试用例清理方法
  • setUpClass():需用户实现,测试类准备方法
  • tearDownClass():需用户实现,测试类清理方法
  • run(result=None):用例运行
  • debug():用例调试
  • skipTest(reason):跳过用例(抛出SkipTest异常)
  • countTestCases():用例数量,固定为1

常用断言方法

image

TestSuite类

常用方法

  • addTest(test):添加单个用例对象到该测试套件
  • addTests(tests):添加多个测试用例对象到该测试套件
  • run(result):测试套件运行
  • countTestCases():测试套件中测用例数量
  • __iter__():支持for ... in ...遍历其中所有的测试用例

TestLoader类

常用方法

  • loadTestsFromTestCase(testCaseClass):通过测试类对象加载其中的所有测试函数,生成测试套件
  • loadTestsFromModule(module, pattern=None):通过测试模块加载其中的所有测试用例,生成测试套件
  • loadTestsFromName(name, module=None):通过字符串格式的测试函数导入路径名,如test_demo.TestDemo.test_a来加载测试用例,生成测试套件
  • loadTestsFromNames(names, module=None):通过测试函数导入路径名,批量加载测试用例,生成测试套件
  • getTestCaseNames(testCaseClass):通过测试类获取其中所有测试函数的测试函数导入路径名,生成测试套件
  • discover(start_dir, pattern='test.py', top_level_dir=None):递归遍历目录,搜集所有test.py中的Test开头的测试类中的所有测试函数,生成测试套件

TestResult类

常用方法

  • wasSuccessful():是否全部成功
  • stop():停止当前测试运行
  • startTest(test):开始(纪录)测试某用例
  • stopTest(test):停止(纪录)测试某用例
  • startTestRun():开始(纪录)整体的测试运行
  • stopTestRun():停止(纪录)整体的测试运行
  • addError(test, err):纪录异常用例
  • addFailure(test, err):纪录失败的用例
  • addSuccess(test):纪录成功的用例(默认什么都不做)
  • addSkip(test, reason):纪录跳过的测试用例
  • addExpectedFailure(test, err):纪录期望失败的测试用例
  • addUnexpectedSuccess(test):纪录非预期成功的测试用例
  • addSubTest(test, subtest, outcome):纪录子测试

TextTestRunner类

  • _makeResult():创建TestResult对象
  • run(test):运行测试套件

Unittest测试框架进阶

使用装饰器为测试函数添加额外属性

class TestCalc(TestCase):
    @test(title='测试加法', priority='p2', status='ready', owner='superhin', iteration='v1.0.0')
    def test_add(self):
        self.logger.info('测试1+1=2')
        self.assertEqual(1 + 1, 2)

使用元类-自动拷贝测试函数属性到测试用例对象

使用用例类属性设置通用属性

class TestDemo(TestCase):
    priority = 'p1'
    status = 'ready'
    owner = 'superhin'
    iteration = 'v0.1.0'

    @test(title='test demo a', priority='p2')
    def test_a(self):
        self.logger.info('a demo test case')

    @test(title='test ddt with data',data=['a', 'b', 'c'])
    def test_b(self, item):
        self.logger.info('item =', item)

测试计划-通过属性筛选测试用例

class TestPlanDemo(TestPlan):
    tests = [
        "test_demo.TestDemo.test_a"
    ]
    filter = {
        "status": ["ready"]
    }

运行控制-超时及失败重试控制

自定义测试套件-实现用例并发

自定义HTML测试报告及JSON测试报告数据实现