自动化测试框架 - Unittest 学习笔记速查


文章目录

      • UnitTest 简介
      • UnitTest 核心
      • UnitTest 原理
      • UnitTest 断言函数
      • TestCase(用例)
        • 基本用法
        • 执行结果
      • TestFixture(夹具)
        • 方法级夹具
        • 类级夹具
        • 模块级夹具
      • TestSuite(套件)
      • TestLoader(加载器)
        • loadTestsFromTestCase(testCaseClass)
        • loadTestsFromModule(module, pattern=None)
        • loadTestsFromNames(names, module=None)
        • suiteClass
        • sortTestMethodsUsing(method)
        • getTestCaseNames(testCaseClass)
      • Discover (加载器)
        • 示例代码
      • TextTestRunner(运行器)
        • 基本用法
        • 自定义输出格式
        • 使用缓冲区
        • 自定义结果类
      • 跳过特定测试用例
        • 示例代码
        • 执行结果
      • BeautifulReport - 运行器扩展
        • 安装
        • 示例代码
        • 执行结果
      • unittestreport - 运行器扩展
        • 安装
        • 示例代码
        • 执行结果
      • DDT - 参数化扩展
        • 什么是DDT(数据驱动)
        • 为什么要使用数据驱动
        • 安装
        • 示例代码
      • parameterized - 参数化扩展
        • 安装
        • 示例代码


UnitTest 简介

在Python的标准库中,unittest框架占据了举足轻重的地位,它是开发者们进行单元测试时不可或缺的利器。
集结了丰富的断言方法与全面的测试工具集,从测试固件、测试套件到测试加载器,无一不为编写清晰、可维护的测试用例提供了强大的后盾。

通过unittest,开发者们可以构建出一套坚实可靠的测试体系,确保代码的正确性和可靠性。
其断言方法的多样性和灵活性,使得测试用例的编写变得游刃有余;
其全面的测试工具集则帮助开发者们更高效地管理、执行和分析测试。

不仅如此,unittest还能与各种第三方工具无缝对接,为开发者们提供更丰富的测试报告和覆盖度分析。
在unittest的支持下,软件开发的整体效率得到了显著提升,代码的质量和稳定性也得到了有力保障。


UnitTest 核心

  • TestCase(测试用例):每个测试用例实例用于封装一个或多个测试函数。
  • TestSuite(测试套件):多个测试用例的集合,用于组织和执行多个测试用例。
  • TestLoader(测试加载器):用于将测试用例加载到测试套件中的工具。
  • TextTestRunner(测试运行器):用于执行测试用例的运行器,负责运行测试并生成结果报告。
  • TestFixture(测试夹具):测试用例的环境搭建和销毁部分,包括前置条件和后置条件。

UnitTest 原理

1.设置测试环境(TestFixture)

  • 通过setUp()方法设置测试用例执行前需要的环境或条件;
  • 如初始化对象、打开文件、建立数据库连接等。
  • 通过tearDown()方法设置测试用例执行后需要的环境清理;
  • 如关闭文件、断开数据库连接、删除数据库记录等。

2.编写测试用例(TestCase)

  • 定义继承自unittest.TestCase的类,并在其中编写具体的测试方法;
  • 每个测试方法通常以test开头,并且不接受任何参数。

3.收集测试用例(TestSuite)

  • 使用TestLoader来加载和收集测试用例,形成一个TestSuite
  • TestSuite是一个测试用例的集合,可以包含多个TestCase实例。

4.执行测试用例(TestRunner)

  • TestRunner负责执行TestSuite中的测试用例;
  • 它运行每个测试用例,并收集测试结果。

5.生成测试报告

  • TestRunner根据测试结果生成报告,通常包括测试通过的用例数、测试失败的用例数、错误信息等内容。

使用TestLoader无需再使用TestSuite,直接收集用例后去送给TestRunner去执行

在这里插入图片描述


UnitTest 断言函数

断言方法断言描述
assertEqual(arg1, arg2, msg=None)验证arg1=arg2,不等则fail
assertNotEqual(arg1, arg2, msg=None)验证arg1 != arg2, 相等则fail
assertTrue(expr, msg=None)验证expr是true,如果为false,则fail
assertFalse(expr,msg=None)验证expr是false,如果为true,则fail
assertIs(arg1, arg2, msg=None)验证arg1、arg2是同一个对象,不是则fail
assertIsNot(arg1, arg2, msg=None)验证arg1、arg2不是同一个对象,是则fail
assertIsNone(expr, msg=None)验证expr是None,不是则fail
assertIsNotNone(expr, msg=None)验证expr不是None,是则fail
assertIn(arg1, arg2, msg=None)验证arg1是arg2的子串,不是则fail
assertNotIn(arg1, arg2, msg=None)验证arg1不是arg2的子串,是则fail
assertIsInstance(obj, cls, msg=None)验证obj是cls的实例,不是则fail
assertNotIsInstance(obj, cls, msg=None)验证obj不是cls的实例,是则fail
assertRaises(expr)验证expr异常类型,结合with使用例子:with self.assertRaises(Exception):  # 测试内容

TestCase(用例)

unittest框架中,TestCase 是一个基础的抽象类,用于定义单个的测试用例。

每个TestCase实例代表一个具体的测试,它会检查某个特定的功能或行为是否符合预期。

使用规则:

  1. 测试用例必须在
  2. 测试用例所在的类,必须继承自unittest.TestCase
  3. 测试用例的方法名称,必须以test开头
  4. 测试用例的方法参数,通常除了self外不接受其他参数
  5. 测试用例的执行顺序是按照ASCILL编码表的排列(0-9,A-Z,a-z…)

基本用法
# 第一步:导入unittest模块
import unittest


# 第二步: 创建一个测试类,被继承unittest.TestCase
class unittest_demo(unittest.TestCase):

    # 第三步:定义测试函数,函数名以test_开头。测试用例
    def test_1(self):
        print("这是第一条测试案例...")

    def test_2(self):
        print("这是第二条测试案例...")

    def test_3(self):
        print("这是第三条测试案例...")

    def test_4(self):
        print("这是第四条测试案例...")


# 第四步:调用unittset.main()方法运行测试用例
if __name__ == 'main':
    unittest.main()


执行结果

在这里插入图片描述


TestFixture(夹具)


方法级夹具
# 第一步:导入unittest模块
import unittest


# 第二步: 创建一个测试类,被继承unittest.TestCase
class unittest_demo(unittest.TestCase):

    # 第三步:重写父类的setUp和tearDown方法
    # 这是每个test case执行前的初始化
    def setUp(self):
        print("setUp方法执行了...")

    # 这是每个test case执行后的测试回收
    def tearDown(self):
        print("tearDown方法执行了...")

    # 第四步:定义测试函数,函数名以test_开头。测试用例
    def test_1(self):
        print("这是第一条测试案例...")

    def test_2(self):
        print("这是第二条测试案例...")

    def test_3(self):
        print("这是第三条测试案例...")

    def test_4(self):
        print("这是第四条测试案例...")


# 第五步:调用unittset.main()方法运行测试用例
if __name__ == 'main':
    unittest.main()

在这里插入图片描述


类级夹具
# 第一步:导入unittest模块
import unittest


# 第二步: 创建一个测试类,被继承unittest.TestCase
class unittest_demo(unittest.TestCase):

    # 第三步:重写父类的setUp和tearDown方法

    # 这是TestCases执行前的初始化
    @classmethod
    def setUpClass(cls):
        print("测试类 Begin...")

    # 这是TestCases执行后的回收操作
    @classmethod
    def tearDownClass(cls):
        print("测试类 End...")

    # 这是每个test case执行前的初始化
    def setUp(self):
        print("setUp方法执行了...")

    # 这是每个test case执行后的测试回收
    def tearDown(self):
        print("tearDown方法执行了...")

    # 第四步:定义测试函数,函数名以test_开头。测试用例
    def test_1(self):
        print("这是第一条测试案例...")

    def test_2(self):
        print("这是第二条测试案例...")

    def test_3(self):
        print("这是第三条测试案例...")

    def test_4(self):
        print("这是第四条测试案例...")


# 第五步:调用unittset.main()方法运行测试用例
if __name__ == 'main':
    unittest.main()

在这里插入图片描述


模块级夹具
# 第一步:导入unittest模块
import unittest


# 模块级别的需要写在 类的外边 直接 定义函数 即可

def setUpModule():
    print("模块前置:", __name__)


def tearDownModule():
    print("模块后置:", __name__)


# 第二步: 创建一个测试类,被继承unittest.TestCase
class unittest_demo1(unittest.TestCase):

    # 第三步:重写父类的setUp和tearDown方法

    # 这是TestCases执行前的初始化
    @classmethod
    def setUpClass(cls):
        print("测试类 Begin...")

    # 这是TestCases执行后的回收操作
    @classmethod
    def tearDownClass(cls):
        print("测试类 End...")

    # 这是每个test case执行前的初始化
    def setUp(self):
        print("setUp方法执行了...")

    # 这是每个test case执行后的测试回收
    def tearDown(self):
        print("tearDown方法执行了...")

    # 第四步:定义测试函数,函数名以test_开头。测试用例
    def test_1(self):
        print("这是第一条测试案例...")

    def test_2(self):
        print("这是第二条测试案例...")

    def test_3(self):
        print("这是第三条测试案例...")

    def test_4(self):
        print("这是第四条测试案例...")


# 将unittest_demo1拷贝一份
class unittest_demo2(unittest.TestCase):

    # 第三步:重写父类的setUp和tearDown方法

    # 这是TestCases执行前的初始化
    @classmethod
    def setUpClass(cls):
        print("测试类 Begin...")

    # 这是TestCases执行后的回收操作
    @classmethod
    def tearDownClass(cls):
        print("测试类 End...")

    # 这是每个test case执行前的初始化
    def setUp(self):
        print("setUp方法执行了...")

    # 这是每个test case执行后的测试回收
    def tearDown(self):
        print("tearDown方法执行了...")

    # 第四步:定义测试函数,函数名以test_开头。测试用例
    def test_1(self):
        print("这是第一条测试案例...")

    def test_2(self):
        print("这是第二条测试案例...")

    def test_3(self):
        print("这是第三条测试案例...")

    def test_4(self):
        print("这是第四条测试案例...")


# 第五步:调用unittset.main()方法运行测试用例
if __name__ == 'main':
    unittest.main()

在这里插入图片描述


TestSuite(套件)

unittest框架中,测试套件(Test Suite)是一个重要的概念;

测试套件允许将多个测试用例、测试套件或测试函数组合到一起,然后一次性运行它们。

用途:

  • 将多个测试用例组合在一起,形成一个逻辑上的测试组。
  • 分层组织测试,例如先运行快速的基础测试,再运行更复杂的集成测试。
  • 动态地添加或移除测试用例。
  • 重复运行同一组测试。

优点:

  • **组织性:**测试套件允许以结构化的方式组织测试用例,使得测试代码更加清晰和易于维护。
  • **灵活性:**可以根据需要动态地添加、删除或修改测试套件中的测试用例。
  • **复用性:**测试套件可以重复使用,例如在不同的测试环境或不同的测试阶段中。
  • **批量执行:**通过测试套件,可以一次性执行多个测试用例,而无需单独运行每个测试用例。

函数:

  • **addTest(test):**将一个测试用例(unittest.TestCase的实例)或测试套件(TestSuite的实例)添加到测试套件中。
  • **addTests(tests):**将多个测试用例或测试套件添加到测试套件中。tests参数通常是一个测试用例或测试套件的列表或元组。
  • **countTestCases():**返回测试套件中包含的测试用例数量。
  • **run(result):**运行测试套件中的所有测试用例,并将结果存储在result参数中(通常是一个unittest.TestResult的实例)。
import unittest


# 定义第一个测试用例类
class TestClass1(unittest.TestCase):
    def test_1_01(self):
        print("执行 test_1_01 测试用例...")
        # 使用断言函数,判断两个参数相等
        self.assertEqual(1 + 1, 2)

    def test_1_02(self):
        print("执行 test_1_02 测试用例...")
        # 使用断言函数,判断一个参数是不是True
        self.assertTrue(True)


# 定义第二个测试用例类
class TestClass2(unittest.TestCase):
    def test_2_01(self):
        print("执行 test_2_01 测试用例...")
        # 使用断言函数,判断参数1是不是参数2的子串
        self.assertIn("hello", "hello world")

    def test_2_02(self):
        print("执行 test_2_02 测试用例...")
        # 使用断言函数,判断参数是不是None
        self.assertIsNone(None)


if __name__ == '__main__':
    # 创建测试套件
    suite = unittest.TestSuite()

    # 将第一个测试用例类的所有测试方法添加到测试套件中
    suite.addTest(unittest.makeSuite(TestClass1))

    # 将第二个测试用例类的一个特定测试方法添加到测试套件中
    suite.addTest(TestClass2("test_2_02"))

    # 运行测试套件
    runner = unittest.TextTestRunner()
    runner.run(suite)

在这里插入图片描述


TestLoader(加载器)

unittest框架中的TestLoader是一个测试用例加载器;
TestLoader的主要任务是发现和加载测试用例,以便之后执行。

TestLoader提供了几个方法用于加载测试用例,并可以根据不同的需求选择使用。


loadTestsFromTestCase(testCaseClass)

用于加载一个特定的测试用例类中的所有测试方法
并将它们作为TestSuite返回

import unittest

class MyTestCase(unittest.TestCase):
    def test_method1(self):
        self.assertEqual(1 + 1, 2)
    
    def test_method2(self):
        self.assertEqual(2 * 2, 4)

# 创建TestLoader实例
loader = unittest.TestLoader()

# 使用loadTestsFromTestCase加载测试用例类中的所有测试方法
suite = loader.loadTestsFromTestCase(MyTestCase)

# 运行测试套件
runner = unittest.TextTestRunner()
runner.run(suite)

loadTestsFromModule(module, pattern=None)

用于加载一个模块中所有符合指定模式的测试用例。
如果不提供pattern,则加载模块中的所有测试用例

import unittest

# 假设有一个名为my_module的模块,其中包含测试用例
# my_module.py
class TestMyModule(unittest.TestCase):
    def test_something(self):
        self.assertEqual(1, 1)

# 在主脚本中加载模块中的测试用例
loader = unittest.TestLoader()

# 加载my_module模块中的所有测试用例
suite = loader.loadTestsFromModule(my_module)

# 运行测试套件
runner = unittest.TextTestRunner()
runner.run(suite)

loadTestsFromNames(names, module=None)

根据提供的名称列表加载测试用例;

这些名称可以是测试用例类名、模块名或可调用对象(例如函数)的名称。

import unittest

# 假设有两个测试用例类
class TestA(unittest.TestCase):
    def test_a(self):
        self.assertEqual(1, 1)

class TestB(unittest.TestCase):
    def test_b(self):
        self.assertEqual(2, 2)

# 创建TestLoader实例
loader = unittest.TestLoader()

# 使用loadTestsFromNames加载特定的测试用例类
suite = loader.loadTestsFromNames(['__main__.TestA', '__main__.TestB'])

# 运行测试套件
runner = unittest.TextTestRunner()
runner.run(suite)

suiteClass

这是一个类属性,它指定了当TestLoader创建新的TestSuite实例时所使用的类。

通常不需要直接设置这个属性,除非你想要自定义TestSuite的行为。

class CustomSuite(unittest.suite.TestSuite):
    # 自定义TestSuite的行为
    pass

class CustomTestLoader(unittest.TestLoader):
    suiteClass = CustomSuite  # 指定使用自定义的TestSuite类

# 使用自定义的TestLoader加载测试用例
loader = CustomTestLoader()
suite = loader.loadTestsFromTestCase(MyTestCase)

sortTestMethodsUsing(method)

这个方法允许指定一个排序函数;
用于对TestCase类中的测试方法进行排序。

def sort_test_methods(a, b):
    # 自定义排序函数
    return a[3:] < b[3:]  # 按方法名字母顺序排序,忽略'test_'前缀

loader = unittest.TestLoader()
loader.sortTestMethodsUsing(sort_test_methods)

suite = loader.loadTestsFromTestCase(MyTestCase)

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

getTestCaseNames(testCaseClass)

这个方法返回一个列表,其中包含testCaseClass中所有以test开头的方法的名称。

这个方法通常不直接调用,而是由TestLoader在内部使用来确定哪些方法应该作为测试用例加载。

loader = unittest.TestLoader()

Discover (加载器)

discover()是unittest框架中的一个功能强大的函数,用于自动发现和运行测试。
该函数会根据给定的开始目录(起始路径),递归地查找该目录及其子目录下的所有测试模块,并加载它们执行测试。

discover() 的用途:

  • 自动发现测试模块:在大型项目中,测试可能分布在多个文件或目录中。discover() 函数可以自动找到这些测试模块,而无需手动导入每个模块。
  • 灵活性和可扩展性:通过指定不同的参数,如模式匹配、顶层目录等,discover() 可以非常灵活地控制哪些测试应该被执行。

discover()的参数:

  • **start_dir:**要搜索测试模块的起始目录
  • **pattern:**匹配测试模块名的模式(默认为 test*.py
  • **top_level_dir:**用于确定要运行的测试的顶层目录(默认为 None,表示使用 start_dir 作为顶层目录)
  • **verbosity:**输出详细信息的级别(默认为 1,表示输出简要结果)
  • **buffer:**控制输出的缓冲(默认为 False
  • **suiteClass:**用于创建测试套件的类(默认为 unittest.TestSuite
  • **loaderClass:**用于加载测试模块的类(默认为 unittest.TestLoader

示例代码

假设有以下目录结构:

project_root/

├── tests/
│ ├── test_module1.py
│ └── test_module2.py

└── main.py

test_module1.pytest_module2.py中分别定义了测试用例。

可以使用以下代码来运行所有测试:

import unittest

# 使用 discover() 函数自动发现并运行测试
unittest.discover(start_dir='./tests', pattern='test*.py')

# 这将运行 tests 目录下所有以 test 开头且扩展名为 .py 的文件中的测试

TextTestRunner(运行器)

TextTestRunner是unittest框架中的一个类,它提供了一个文本模式的测试执行器,用于运行测试用例并输出结果;

TextTestRunner 可以配置以显示详细的测试输出,包括测试进度、测试成功或失败的信息等。

TextTestRunner 的构造函数接受几个参数来定制其行为:

  • **stream:**一个输出流对象,用于写入测试结果。默认为sys.stdout,表示标准输出。
  • **descriptions:**一个布尔值,指定是否打印每个测试用例的描述。默认为 True
  • **verbosity:**一个整数,指定测试输出的详细程度。0表示最少输出,1表示正常输出,2表示详细输出。默认为1。
  • **failfast:**一个布尔值,如果为True,则一旦有测试失败,就立即停止运行后面的测试。默认为False
  • **buffer:**一个布尔值,如果为True,则输出会被缓冲,直到测试运行结束。默认为False
  • **resultclass:**一个TestResult类的子类,用于处理测试结果。默认为 unittest.TextTestResult

基本用法

TextTestRunner 的主要方法是 run(test)
它接受一个TestSuiteTestCase实例作为参数;
运行测试并返回测试结果。

import unittest

# 定义测试用例类
class MyTestCase(unittest.TestCase):
    def test_addition(self):
        self.assertEqual(1 + 1, 2)

    def test_subtraction(self):
        self.assertEqual(2 - 1, 1)

# 创建测试套件
suite = unittest.TestSuite()
suite.addTest(MyTestCase('test_addition'))
suite.addTest(MyTestCase('test_subtraction'))

# 创建TextTestRunner实例
runner = unittest.TextTestRunner()

# 运行测试套件并输出结果
result = runner.run(suite)

# 输出测试结果
print("Test result:", result.wasSuccessful())
自定义输出格式

可以通过修改TextTestRunnerverbosity参数来定制输出信息的详细程度。

runner = unittest.TextTestRunner(verbosity=2)
# verbosity=0: 最简洁的输出
# verbosity=1: 标准输出
# verbosity=2: 详细输出

使用缓冲区

buffer参数可以决定输出是否缓冲;
如果设置为True,则所有输出都会被缓冲,直到测试运行完成

runner = unittest.TextTestRunner(buffer=True)

自定义结果类

resultclass 参数允许你使用自定义的结果类来处理测试结果

class CustomResult(unittest.TextTestResult):
    # 在这里可以添加自定义逻辑
    pass

runner = unittest.TextTestRunner(resultclass=CustomResult)

跳过特定测试用例

使用 @unittest.skip(reason) 强制跳过测试用例,并提供一个跳过的原因

import unittest

class MyTestCases(unittest.TestCase):
    @unittest.skip("这个测试用例暂时不被需要或尚未完成")
    def test_skipped_due_to_reason(self):
        self.assertTrue(False, "这个测试不会执行,因为它被跳过了")

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

使用@unittest.skipIf(condition, reason)条件成立时跳过测试用例,并提供一个跳过的原因

import unittest
import sys

class MyTestCases(unittest.TestCase):
    @unittest.skipIf(sys.version_info < (3, 6), "这个测试需要Python 3.6或更高版本")
    def test_python_version(self):
        self.assertTrue(True, "这个测试在Python 3.6或更高版本下执行")

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

使用@unittest.skipUnless(condition, reason)条件不成立时跳过测试用例,并提供一个跳过的原因

import unittest

class MyTestCases(unittest.TestCase):
    @unittest.skipUnless(False, "这个测试被故意设置为总是跳过")
    def test_always_skipped(self):
        self.assertTrue(True, "这个测试不会执行,因为它被跳过了")

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

使用@unittest.expectedFailure标记一个测试用例预期失败

import unittest

class MyTestCases(unittest.TestCase):
    @unittest.expectedFailure
    def test_expected_to_fail(self):
        self.assertTrue(False, "这个测试预期会失败,因为它尚未完成或存在已知问题")

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

使用self.skipTest()函数,跳过当前正在执行的测试用例

这通常在测试方法的内部逻辑中基于某些条件决定跳过测试时使用。

import unittest

class MyTestCases(unittest.TestCase):
    def test_skipping_in_method(self):
        # 基于某种条件决定跳过测试
        if not some_external_condition:  # 假设 some_external_condition 是一个外部条件或变量
            self.skipTest("由于某些外部条件,跳过此测试")
        self.assertTrue(True, "这个测试执行了")

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

示例代码
import unittest
import sys


class SkippingTestCases(unittest.TestCase):

    # 使用 @unittest.skip 强制跳过整个测试方法
    @unittest.skip("这个测试方法:test_skip_by_decoration 被强制跳过了")
    def test_skip_by_decoration(self):
        self.fail("这个测试方法被跳过了")

    # 使用条件判断在测试方法内部跳过
    def test_skip_in_method_by_condition(self):
        if sys.version_info < (3, 6):
            self.skipTest("这个测试需要Python 3.6或更高版本")
        self.assertTrue(True, "这个测试在Python 3.6或更高版本下执行")

    # 使用 @unittest.skipIf 根据条件在装饰时跳过
    @unittest.skipIf(sys.version_info < (3, 6), "这个测试需要Python 3.6或更高版本")
    def test_skip_if_condition_met(self):
        self.assertTrue(True, "这个测试在Python 3.6或更高版本下执行")

    # 使用 @unittest.skipUnless 根据条件在装饰时跳过,当条件不满足时跳过
    @unittest.skipUnless(False, "这个测试方法:test_skip_unless_condition_not_met 被故意设置为总是跳过")
    def test_skip_unless_condition_not_met(self):
        self.fail("这个测试不会执行,因为它被跳过了")

    # 使用 @unittest.expectedFailure 标记预期会失败的测试
    @unittest.expectedFailure
    def test_expected_to_fail(self):
        self.assertTrue(True, "这个测试预期会失败,因为它尚未完成或存在已知问题")


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

执行结果

在这里插入图片描述


BeautifulReport - 运行器扩展


安装

BeautifulReport 属于第三方库,需要额外下载安装,命令如下:

pip install BeautifulReport

示例代码
import unittest

from BeautifulReport import BeautifulReport


# 定义一个测试类
class MathTests(unittest.TestCase):

    # 测试加法
    def test_addition(self):
        self.assertEqual(1 + 1, 3)

    # 测试减法
    def test_subtraction(self):
        self.assertEqual(2 - 1, 1)

    # 测试乘法
    def test_multiplication(self):
        self.assertEqual(2 * 2, 5)

    # 测试除法
    def test_division(self):
        self.assertEqual(4 / 2, 2)

    # 测试除法,预期会失败
    def test_division_by_zero(self):
        with self.assertRaises(ZeroDivisionError):
            1 / 0


# 定义另一个测试类
class StringTests(unittest.TestCase):

    # 测试字符串连接
    def test_string_concatenation(self):
        self.assertEqual("Hello, " + "World", "Hello, World")

    # 测试字符串相等
    def test_string_equality(self):
        self.assertEqual("Hello", "Hello")

    # 测试字符串不相等
    def test_string_inequality(self):
        self.assertNotEqual("Hello", "Goodbye")


# 如果这个脚本被直接运行,则执行测试
if __name__ == '__main__':
    # 创建测试套件,添加测试类
    suite = unittest.TestSuite()
    suite.addTest(unittest.makeSuite(MathTests))
    suite.addTest(unittest.makeSuite(StringTests))

    # 利用BeautifulReport生成测试报告
    result = BeautifulReport(suite)
    result.report(filename='这是测试报告的文件名称',
                  description='这是测试报告的描述',
                  log_path="./test_reports/log.log",
                  report_dir="./test_reports")


执行结果

在这里插入图片描述


unittestreport - 运行器扩展


安装

unittestreport 属于第三方库,需要额外下载安装,命令如下:

pip install unittestreport

示例代码
import unittest

from unittestreport import TestRunner


# 定义一个测试类
class MathTests(unittest.TestCase):

    # 测试加法
    def test_addition(self):
        self.assertEqual(1 + 1, 3)

    # 测试减法
    def test_subtraction(self):
        self.assertEqual(2 - 1, 1)

    # 测试乘法
    def test_multiplication(self):
        self.assertEqual(2 * 2, 5)

    # 测试除法
    def test_division(self):
        self.assertEqual(4 / 2, 2)

    # 测试除法,预期会失败
    def test_division_by_zero(self):
        with self.assertRaises(ZeroDivisionError):
            1 / 0


# 定义另一个测试类
class StringTests(unittest.TestCase):

    # 测试字符串连接
    def test_string_concatenation(self):
        self.assertEqual("Hello, " + "World", "Hello, World")

    # 测试字符串相等
    def test_string_equality(self):
        self.assertEqual("Hello", "Hello")

    # 测试字符串不相等
    def test_string_inequality(self):
        self.assertNotEqual("Hello", "Goodbye")


# 如果这个脚本被直接运行,则执行测试
if __name__ == '__main__':
    # 创建测试套件,添加测试类
    suite = unittest.TestSuite()
    suite.addTest(unittest.makeSuite(MathTests))
    suite.addTest(unittest.makeSuite(StringTests))

    # 利用unittestreport生成测试报告
    runner = TestRunner(suite,
                        filename='report.html',
                        report_dir="./test_reports",
                        title='加减乘除和字符串案例的测试报告',
                        tester="我是测试开发的烦恼",
                        desc='测试加减乘除、字符串等案例')
    runner.run()


执行结果

在这里插入图片描述


DDT - 参数化扩展


什么是DDT(数据驱动)
  • DDT又叫数据驱动(Data-Driven Design)
  • 通常用于将测试数据与函数代码进行分离,存储某个位置的文件中;在自动化测试中数据驱动框架会读取文件中的数据,然后把数据作为参数传递到功能函数中,并会根据数据的条目数量多次运行同一个功能函数;
  • 这样做的好处就是提高代码的复用性、可维护性;
  • 数据驱动的数据文件可以是:txt、csv、Excel、json、yaml等文件

为什么要使用数据驱动

数据与代码分离

  • 通过将测试数据与函数代码分离,可以使测试代码更加清晰和易于维护。
  • 测试数据通常存储在外部文件中,如txt、csv、Excel、json、yaml等,这样可以在不修改代码的情况下更改测试数据。

提高代码复用性

  • 使用数据驱动框架,可以多次运行同一个功能函数,每次使用不同的测试数据。> - 这样,相同的测试逻辑可以被重复使用,提高了代码的复用性。

提高可维护性

  • 当测试数据发生变化时,只需要更新存储测试数据的文件,而不需要修改测试代码。
  • 这降低了维护成本,并提高了测试脚本的可维护性。

灵活性

  • 数据驱动测试允许测试人员快速添加、删除或修改测试数据,以适应不同的测试场景和需求。

易于扩展

  • 由于测试数据与代码是分离的,因此可以更容易地扩展测试范围,包括增加新的测试数据或添加新的测试场景。

安装

DDT 属于第三方库,需要额外下载安装,命令如下:

pip install ddt

示例代码

传递一个参数

# 导入unittest框架、ddt驱动模块、data数据传递模块
import unittest
from ddt import ddt
from ddt import data

# 声明TestMyCase()类使用ddt数据驱动
@ddt
class TestMyCase(unittest.TestCase):
    # 使用@data装饰器将声明的数据传递给测试用例方法中的形参
    @data('18900001234','15800001234','13900001234')
    # phone(1个形参):接收data传递来的数据
    # 因为只有一个形参传递进来的数据都是给phone使用的
    def test_01_phone(self,phone):
        print('手机号:',phone)

传递多个参数

# 导入unittest框架、ddt驱动模块、data数据传递模块、unpack拆分传递的数据包模块
import unittest
from ddt import ddt
from ddt import data
from ddt import unpack


# 声明TestMyCase()类使用ddt数据驱动
@ddt
class TestMyCase(unittest.TestCase):
    # 使用@data装饰器将声明的数据传递给测试用例方法中的形参
    @data(['admin','123456','78oa'],['guest','111111','a3b6'])
    # username,password,code(3个形参):接收data传递来的数据
    # 因为存在多个形参,解释器并不知道将哪个数据传递给哪个形参,所以需要先将数据拆解
    # 将data中的第一个数据包拆解开来,分别传递给形参,再接着依次拆解数据包分配给对应的形参
    # 使用@unpack将传递进来的参数进行拆解,分别传递形参
    @unpack
    def test_02_login(self,username,password,code):
        print('账号:',username,'密码:',password,'验证码:',code)

使用ddt读取txt文件内容传递

# phone.txt内容
----------------------------------
15800001111
15600002222
18900003333
13900004444
18011115555
13500006666

# userdata.txt内容
----------------------------------
admin,123456,s7c8
guest,111111,08hg
visitor,000000,kl0s

# ddt_demo.py内容
----------------------------------
# 导入unittest框架、ddt驱动模块、data数据传递模块、unpack拆分传递的数据包模块
import unittest
from ddt import ddt
from ddt import data
from ddt import unpack

"""
with 语句实质是上下文管理。
1、上下文管理协议。包含方法__enter__() 和 __exit__(),支持该协议对象要实现这两个方法。
2、上下文管理器,定义执行with语句时要建立的运行时上下文,负责执行with语句块上下文中的进入与退出操作。
3、进入上下文的时候执行__enter__方法,如果设置as var语句,var变量接受__enter__()方法返回值。
4、如果运行时发生了异常,就退出上下文管理器。调用管理器__exit__方法。
"""

'''
    读取phone.txt文件获取数据作为参数
    1、声明phone列表用来存储读取并处理后的数据
    2、使用with机制以utf-8编码格式用只读的模式open文件
    3、循环遍历文件中的所有行,将每一行去除空白符添加到列表中
    4、返回phone列表
'''

def read_phone():
    phone = []
    with open(file='phone.txt',mode='r',encoding='utf-8') as f:
        for lina in f.readlines():
            phone.append(lina.strip())
    return phone

'''
    读取userdata.txt文件获取数据作为参数
    1、声明user_info列表用来存储读取并处理后的数据
    2、使用with机制以utf-8编码格式用只读的模式open文件
    3、循环遍历文件中的所有行,将每一行去除空白符后以','分割成列表
    4、将处理后的行添加到user_info列表并返回
'''
def read_userdata():
    user_info = []
    with open(file='userdata.txt',mode='r',encoding='utf-8') as u:
        for rows in u.readlines():
            rows_str = str(rows.strip()).rsplit(',')
            user_info.append(rows_str)
    return user_info


# 声明TestMyCase()类使用ddt数据驱动
@ddt
class TestMyCase(unittest.TestCase):
    # 使用data装饰器,调用read_phone()函数用其返回值传递给测试用例的形参
    # 因为文件中含有多条数据,需要使用动态参数传递
    # 否则就会被看成一条数据,只会执行一条用例
    @data(*read_phone())
    # phone(1个形参):接收data传递来的数据
    def test_01_phone(self,phone):
        print('手机号:',phone)

    # 使用data装饰器,调用read_userdata()函数用其返回值传递给测试用例的形参
    # 因为文件中含有多条数据,需要使用动态参数传递
    # 否则就会被看成一条数据,只会执行一条用例
    @data(*read_userdata())
    # username,password,code(3个形参):接收data传递来的数据
    # 因为存在多个形参,解释器并不知道将哪个数据传递给哪个形参,所以需要先将数据拆解
    # 将data中的第一个数据包拆解开来,分别传递给形参,再接着依次拆解数据包分配给对应的形参
    # 使用@unpack将传递进来的参数进行拆解,分别传递形参
    @unpack
    def test_02_login(self,username,password,code):
        print('账号:',username,'密码:',password,'验证码:',code)

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


使用ddt读取json数据文件内容传递

# phone.txt内容
----------------------------------
[
  "15800001111",
  "15600002222",
  "18900003333"
]

# userdata.txt内容
----------------------------------
[
  {
    "username":"admin",
    "password":"123456",
    "code":"s7c8"
  },

  ["guest","111111","08hg"]
]

# ddt_demo.py内容
----------------------------------
# 导入unittest框架、ddt驱动模块、data数据传递模块、unpack拆分传递的数据包模块、json模块
import unittest
from ddt import ddt
from ddt import data
from ddt import unpack
import json

'''
    使用json类对象调用load加载函数从phone.json、userdata.json数据文件加载数据
'''
def read_phone():
    return json.load(open(file='phone.json',mode='r',encoding='utf-8'))

def read_userdata():
    return json.load(open(file='userdata.json',mode='r',encoding='utf-8'))


#  声明TestMyCase()类使用ddt数据驱动
@ddt
class TestMyCase(unittest.TestCase):
    # 使用data装饰器,调用read_phone()函数用其返回值传递给测试用例的形参
    # 因为文件中含有多条数据,需要使用动态参数传递
    # 否则就会被看成一条数据,只会执行一条用例
    @data(*read_phone())
    # phone(1个形参):接收data传递来的数据
    def test_01_phone(self,phone):
        print('手机号:',phone)

    # 使用data装饰器,调用read_userdata()函数用其返回值传递给测试用例的形参
    # 因为文件中含有多条数据,需要使用动态参数传递
    # 否则就会被看成一条数据,只会执行一条用例
    @data(*read_userdata())
    # username,password,code(3个形参):接收data传递来的数据
    # 因为存在多个形参,解释器并不知道将哪个数据传递给哪个形参,所以需要先将数据拆解
    # 将data中的第一个数据包拆解开来,分别传递给形参,再接着依次拆解数据包分配给对应的形参
    # 使用@unpack将传递进来的参数进行拆解,分别传递形参
    @unpack
    def test_02_login(self,username,password,code):
        print('账号:',username,'密码:',password,'验证码:',code)

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


parameterized - 参数化扩展

parameterized是python的第三方库,用于为unittest测试提供参数化支持。

它允许为测试用例提供多个参数集,以便多次运行相同的测试逻辑,但每次使用不同的输入参数。


安装

parameterized 属于第三方库,需要额外下载安装,命令如下:

pip install parameterized

示例代码
from parameterized import parameterized
import unittest

class TestParameterized(unittest.TestCase):

    # 单个参数
    @parameterized.expand([
        ("foo",),
        ("bar",),
    ])
    def test_with_single_parameter(self, s):
        self.assertIn(s, ["foo", "bar", "baz"])

    # 多个参数
    @parameterized.expand([
        (1, 2, 3),
        (4, 5, 9),
    ])
    def test_with_multiple_parameters(self, a, b, expected_sum):
        self.assertEqual(a + b, expected_sum)

    # 列表参数
    @parameterized.expand([
        ([1, 2, 3],),
        ([4, 5, 6],),
    ])
    def test_with_list_parameter(self, lst):
        self.assertEqual(sum(lst), sum([i for i in lst]))

    # 字典参数
    @parameterized.expand([
        ({"a": 1, "b": 2, "expected": 3},),
        ({"a": -1, "b": 1, "expected": 0},),
    ])
    def test_with_dict_parameter(self, kwargs):
        self.assertEqual(kwargs["a"] + kwargs["b"], kwargs["expected"])

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/526542.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

文件/目录的权限和归属

一.文件/目录的权限和归属的简要分析与概括 1.程序访问文件时的权限&#xff0c;取决于此程序的发起者 * 进程的发起者&#xff0c;同文件的属主&#xff1a;则应用文件属主权限 *进程的发起者&#xff0c;属于文件属组&#xff1b;则应用文件属组权限 *应用文件“其它”权…

【云贝教育】Oracle 19c OCP 083题库解析(71)

本文为云贝教育郭一军&#xff08;微信&#xff1a;guoyJoe&#xff09;原创&#xff0c;请尊重知识产权&#xff0c;转发请注明出处&#xff0c;不接受任何抄袭、演绎和未经注明出处的转载。 71、Which two are true about an Oracle gold image-based installation in Oracle…

指尖论文靠谱不 #学习方法#媒体#职场发展

指尖论文是当前市场上一款非常实用的论文写作、查重、降重工具。许多学生、研究人员和教师都已经开始使用它&#xff0c;因为它相当靠谱、方便、易用且高效。 首先&#xff0c;指尖论文的查重功能非常强大&#xff0c;能够及时准确地检测出文本中的相似内容&#xff0c;帮助用户…

源清天木生物科技带您抢先体验2024国际生物发酵展

参展企业介绍 优质的种质资源是生物产业的核心&#xff0c;也是农业的核心&#xff01; 高效的选育优质的种质资源&#xff0c;是生物产业和农业最重要工作之一。 天木生物&#xff0c;致力于高效生物育种技术开发和特色生物育种装备开 发&#xff0c;并依托自主研发的技术和装…

揭秘强大的文件同步利器Rsycn

目录 引言 一、Rsycn基础介绍 &#xff08;一&#xff09;基本概念 &#xff08;二&#xff09;特性 &#xff08;三&#xff09;同步方式 &#xff08;四&#xff09;服务备份角色 &#xff08;五&#xff09;命令工具 &#xff08;六&#xff09;配置格式 &#xff…

[C++][C++11][六] -- [线程库]

目录 1.thread类的简单介绍2.线程对象的构造方法1.无参构造2.带参构造3.移动构造4.注意 3.thread提供的成员函数4.获取线程id5.线程函数的参数问题1.指针2.借助std::ref函数3.借助lambda表达式 6.join和detach1.join()2.detach() 7.[mutex](http://在C11中&#xff0c;Mutex总共…

OAuth2.0客户端和服务端Java实现

oauth2 引言 读了《设计模式之美》和《凤凰架构》架构安全篇之后&#xff0c;决定写一个OAuth2.0的认证流程的Demo&#xff0c;也算是一个阶段性的总结&#xff0c;具体原理实现见《凤凰架构》(架构安全设计篇)。 涉及到的源码可以从https://github.com/WeiXiao-Hyy/oauth2获…

智慧农场物联网系统:重塑农业的未来

随着科技的进步&#xff0c;物联网技术正在逐渐改变我们的生活。在农业领域&#xff0c;物联网系统也正在发挥着越来越重要的作用&#xff0c;为智慧农场的发展提供了新的可能。本文将深入探讨智慧农场物联网系统的优势、应用场景、技术实现以及未来发展趋势。 一、智慧农场物…

Java Web这一路走来

大部分Java应用都是Web或网络应用&#xff0c;MVC框架在Java框架中有着举足轻重的地位&#xff0c;一开始的Web应用并不现在这样子的&#xff0c;一步一步走来&#xff0c;每一步都经历了无数的血和泪的教训&#xff0c;以史为镜可以知兴替。 1. 草莽时代 早期的Java服务端技…

只有线上出了bug,老板们才知道测试的价值?

有同学说&#xff0c;测试没价值&#xff0c;我们测试团队刚被拆散了。 也有同学说&#xff0c;公司不重视测试&#xff0c;我觉得我们就是测试得太好了。哪天线上出个bug&#xff0c;老板们就知道测试的价值了。 还有人给测试同学规划职业发展路径&#xff0c;就是不做测试&…

蓝桥杯算法题:练功

【问题描述】 小明每天都要练功&#xff0c;练功中的重要一项是梅花桩。 小明练功的梅花桩排列成 n 行 m 列&#xff0c;相邻两行的距离为 1&#xff0c;相邻两列的距离也为 1。 小明站在第 1 行第 1 列上&#xff0c;他要走到第 n 行第 m 列上。小明已经练了一段时间&#xff…

【IC前端虚拟项目】spyglass lint环境组织与lint清理

【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 和上个虚拟项目的lint清理环节一样&#xff0c;关于spyglass的lint清理功能与流程还是大家通过各种资料去学习下就好啦。和之前不同的事&#xff0c;这次的虚拟项目里我把流程封装为Makefile&#xff0c;…

中老年人购买需求不断上升,下一个电商新风口又会在哪里?

大家好&#xff0c;我是电商花花。 电商互联网时代&#xff0c;不再是年轻人的刚需。 中老年消费市场与日俱增&#xff0c;据统计&#xff0c;我国中老年人口已近10亿&#xff0c;占比巨大&#xff0c;且呈上升趋势。 50、60后的中老年人入网率已然在加快提升&#xff0c;相…

提示工程中的10个设计模式

我们可以将提示词定义为向大型语言模型(Large Language Model&#xff0c;LLM)提供的一个查询或一组指令&#xff0c;这些指令随后使模型能够维持一定程度的自定义或增强&#xff0c;以改进其功能并影响其输出。我们可以通过提供细节、规则和指导来引出更有针对性的输出&#x…

HDLbits 刷题 --Reduction

Given a 100-bit input vector [99:0], reverse its bit ordering. 译&#xff1a; 给定一个100位的输入向量 [99:0]&#xff0c;反转其位的顺序。 module top_module( input [99:0] in,output [99:0] out );integer i;always(*) beginfor(i0;i<100;i)out[99-i]in[i];end…

如何使用ChatGPT制作一个休闲小游戏集合的网站?

1. 先看制作的网站效果 先预览一个使用ChatGPT制作的休闲小游戏集合网站&#xff1a;CasualMiniGame.com 2. 使用描述词生成网站首页 使用ChatGPT生成一个在线的休闲小游戏网站是一项有趣的任务。ChatGPT可以生成代码&#xff0c;展示您输入的描述词对应的网站效果。以下是Ch…

Metasploit Pro 4.22.3-2024040301 (Linux, Windows) - 专业渗透测试框架

Metasploit Pro 4.22.3-2024040301 (Linux, Windows) - 专业渗透测试框架 Rapid7 Penetration testing, Release Apr 03, 2024 请访问原文链接&#xff1a;Metasploit Pro 4.22.3-2024040301 (Linux, Windows) - 专业渗透测试框架&#xff0c;查看最新版。原创作品&#xff0…

希亦、添可、追觅洗地机深入对比,哪个更胜一筹?选择不再迷茫

洗地机不仅能快速打扫地面&#xff0c;省时又省力&#xff0c;还能把室内弄得干干净净的&#xff0c;用起来感觉特舒服。但是面对目前市面上几百个型号的洗地机&#xff0c;在观望洗地机的家庭&#xff0c;大多数都是不确定哪款洗地机适合自己&#xff0c;今天笔者就带来了三款…

【NLP练习】中文文本分类-Pytorch实现

中文文本分类-Pytorch实现 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、准备工作 1. 任务说明 本次使用Pytorch实现中文文本分类。主要代码与文本分类代码基本一致&#xff0c;不同的是本次任务使用…

【学习心得】Python中的queue模块使用

一、Queue模块的知识点思维导图 二、Queue模块常用函数介绍 queue模块是内置的&#xff0c;不需要安装直接导入就可以了。 &#xff08;1&#xff09;创建一个Queue对象 import queue# 创建一个队列实例 q queue.Queue(maxsize20) # 可选参数&#xff0c;默认为无限大&am…