蜗牛学院测试开发学习笔记--UI自动化3

unittest框架

什么是unittest框架

unittest 是 Python 标准库中的一个单元测试框架,它提供了编写和运行自动化测试的机制。单元测试是软件开发中的一种测试方法,用于验证代码的最小可测试部分(通常是函数或方法)是否按预期工作。

unittest 框架的核心特性包括:

  1. 测试用例:通过继承 unittest.TestCase 类来创建测试用例。每个测试方法都以 test 开头,用于定义一个单独的测试。
  2. 断言方法unittest 提供了一系列断言方法,如 assertEqualassertTrueassertFalse 等,用于验证测试结果是否符合预期。
  3. 测试套件:可以使用 unittest.TestSuite 来组织多个测试用例或测试套件。
  4. 测试运行:可以通过命令行工具 unittest 运行测试,或者在代码中使用 unittest.main() 函数来启动测试。
  5. 测试发现unittest 支持自动发现测试用例,它会查找指定目录下所有继承自 unittest.TestCase 的类,并自动运行这些类中以 test 开头的方法。
  6. 测试夹具unittest 提供了夹具(fixture)功能,允许你在每个测试方法之前和之后执行代码,例如设置和清理测试环境。这可以通过 setUptearDown 方法来实现。
  7. 测试结果:测试结果可以输出到控制台,也可以通过其他方式(如 XML 报告)进行记录,方便持续集成系统使用。

使用 unittest 框架可以帮助开发者确保代码质量,及时发现和修复错误,是 Python 开发中常用的测试工具之一。

案例

项目代码

# 项目代码
class count:
    def __init__(self, a, b):
        self.a = a
        self.b = b
    # 计算加法
    def add(self):
        return self.a + self.b
    #  减法
    def Subtraction(self):
        return self.a - self.b

测试代码:

import unittest
from day04.calc import count
'''
unittest是python的单元测试框架,不需要安装直接导入使用即可
测试用例类需要继承unittest.TestCase
属性该类的测试用例其实就是一个实例方法
该测试用例方法,必须要使用test开头
CountTest.main():执行该测试类中所有测试用例
'''
# 测试用例
class CountTest(unittest.TestCase):
    # 测试加法
    def test_add(self):
        # 调用项目代码中的加法
        # 类的实例化
        c1 = count(5, 6)
        # 调用方法
        r = c1.add()
        # 测试的本质永远是实际结果和需求结果的比较
        # 断言
        self.assertEqual(r, 12)
    # 测试减法
    def test_sub(self):
        # 类的实例化
        c2 = count(6, 3)
        # 调用减法
        r = c2.Subtraction()
        # 断言
        self.assertEqual(r, 3)
if __name__ == '__main__':
    # 执行测试用例(执行该测试类下面的所有测试用例)
    CountTest.main()

unittest中的断言有哪些类型,它们各自有什么用途?

unittest 框架中,断言(assertions)是用来验证测试用例中的条件是否为真。如果断言的条件为真,则测试通过;如果为假,则测试失败,并报告一个错误。unittest 提供了多种断言方法,每种方法都用于检查不同类型的条件。以下是一些常用的断言方法及其用途:

  1. assertEqual(a, b)
    • 检查两个值是否相等。如果 a 不等于 b,则测试失败。
  2. assertNotEqual(a, b)
    • 检查两个值是否不相等。如果 a 等于 b,则测试失败。
  3. assertTrue(x)
    • 检查一个条件是否为真。如果 x 为假(如 FalseNone0、空字符串等),则测试失败。
  4. assertFalse(x)
    • 检查一个条件是否为假。如果 x 为真,则测试失败。
  5. assertIs(a, b)
    • 检查两个对象是否是同一个对象。如果 ab 不是同一个对象,则测试失败。
  6. assertIsNot(a, b)
    • 检查两个对象是否不是同一个对象。如果 ab 是同一个对象,则测试失败。
  7. assertIsNone(x)
    • 检查一个对象是否是 None。如果 x 不是 None,则测试失败。
  8. assertIsNotNone(x)
    • 检查一个对象是否不是 None。如果 xNone,则测试失败。
  9. assertIn(a, b)
    • 检查一个元素是否在容器(如列表、元组、集合、字典等)中。如果 a 不在 b 中,则测试失败。
  10. assertNotIn(a, b)
    • 检查一个元素是否不在容器中。如果 ab 中,则测试失败。
  11. assertIsInstance(a, b)
    • 检查一个对象是否是指定类型的实例。如果 a 不是 b 类型的实例,则测试失败。
  12. assertNotIsInstance(a, b)
    • 检查一个对象是否不是指定类型的实例。如果 ab 类型的实例,则测试失败。
  13. assertAlmostEqual(a, b)
    • 检查两个浮点数是否近似相等。通常用于比较浮点数,因为浮点数计算可能会有精度问题。
  14. assertNotAlmostEqual(a, b)
    • 检查两个浮点数是否不近似相等。
  15. assertGreater(a, b)
    • 检查 a 是否大于 b
  16. assertGreaterEqual(a, b)
    • 检查 a 是否大于或等于 b
  17. assertLess(a, b)
    • 检查 a 是否小于 b
  18. assertLessEqual(a, b)
    • 检查 a 是否小于或等于 b
  19. assertRaises(exception)
    • 检查在上下文管理器或函数调用中是否抛出了指定的异常。
  20. assertRegex(s, r)
    • 检查字符串 s 是否与正则表达式 r 匹配。

这些断言方法提供了丰富的测试验证手段,使得测试用例可以覆盖各种不同的验证需求。使用这些断言方法,你可以确保你的代码在各种条件下都能正确地工作。

二、unittest模块说明

  1. TestCase:一个TestCase的实例就是一个测试用例,是一个完整的测试流程,包括测试前准备环境的搭建(setUp),实现测试过程的代码(run),测试后环境的还原(tearDown).
  2. Test Suite:把多个测试用例集合在一起来执行。可以通过addTest加载TestCase到Test Suite(测试套件)中,从而返回一个TestSuite实例。
  3. Test Runner:测试的执行,通过TextTestRunner类提供的run()方法来执行Test Suite/TestCase。Test Runner可以使用图形界面,文本界面,或者返回一个特殊的值的方式来表示测试执行的结果。
  4. Test Fixture:对一个测试用例环境的初始化和清除。通过覆盖TestCase的setUp()和tearDown()方法来实现。tearDown()为下一个测试用例提供一个干净的环境。

三、常见的断言方式

# 如果两个值相等,则测试通过
assertEqual(r, 12)
# 如果两个值不相等,则测试通过
assertNotEqual(r, 12)
# 判断返回值是否为true,如果为true则测试通过,msg后面可以自定义失败信息,可以写也可以不写
self.assertTrue(r, msg="测试不通过")
#.判断第一个参数是否在第二个参数中,简言之也就是检查第二个参数是否包含第一个参数。
self.assertIn(r, new_names)
# 如果第一个值不在第二个值中,则测试通过
self.assertNotIn(r, new_names)
import random
# 项目代码
class count:
    def __init__(self, a, b):
        self.a = a
        self.b = b
    # 计算加法
    def add(self):
        return self.a + self.b
    #  减法
    def Subtraction(self):
        return self.a - self.b
    # 返回true或者false
    def func1(self):
        if self.a - self.b >= 10:
            return True
        else:
            return False
    def func2(self):
        names = ["张三1", '李四1', '王五1', "赵六1"]
        return random.choice(names)
import unittest
from day04.calc import count
'''
unittest是python的单元测试框架,不需要安装直接导入使用即可
测试用例类需要继承unittest.TestCase
属性该类的测试用例其实就是一个实例方法
该测试用例方法,必须要使用test开头
'''
# 测试用例
class CountTest(unittest.TestCase):
    # 测试加法
    def test_add(self):
        # 调用项目代码中的加法
        # 类的实例化
        c1 = count(5, 6)
        # 调用方法
        r = c1.add()
        # 测试的本质永远是实际结果和需求结果的比较
        # 断言
        self.assertNotEqual(r, 12)
    # 测试减法
    def test_sub(self):
        # 类的实例化
        c2 = count(6, 3)
        # 调用减法
        r = c2.Subtraction()
        # 断言
        self.assertEqual(r, 3)
    def test_func1(self):
        # 类的实例化
        c3 = count(7, 5)
        # false
        r = c3.func1()
        self.assertTrue(r, msg="测试不通过")
    def test_func2(self):
        # 类的实例化
        c4 = count(4,5)
        # 调用方法
        r = c4.func2()
        new_names = ["张三", '李四', '王五', "赵六", "张无忌", "周芷若", "赵敏"]
        # 取出某个人名,判断其在不在某个值中
        # self.assertIn(r, new_names)
        self.assertNotIn(r, new_names)
if __name__ == '__main__':
    # 执行测试用例(执行该测试类下面的所有测试用例)
    CountTest.main()

四、Test Fixture(对测试环境的初始化和清除)

测试用例依赖的测试数据准备活动就可以在环境的初始化完成,清除的时候就是初始化添加了什么数据,清除的时候就需要将什么数据给删除掉

  • setUpModule/tearDownModule:在整个模块的开始和结束时被执行。

    _______________这是setUpModule_____________
    ***********test_add************
    ***********test_sub************
    ***********test_func1************
    ***********test_func2************
    _______________这是tearDownModule_____________
    
  • setUpClass/tearDownClass: 在测试类的开始和结束时被执行。

    针对某个具体测试类而言的

    _______________这是setUpModule_____________
    _______________这是setUpClass_____________
    ***********test_add************
    ***********test_sub************
    ***********test_func1************
    ***********test_func2************
    _______________这是tearDownClass_____________
    _______________这是tearDownModule_____________
    
    
  • setUp/tearDown:在测试用例的开始与结束时被执行 注意:setUpClass/tearDownClass的写法稍有不同,首先通过@classmethod进行装饰,其次方法的参数为cls,也可以是别的。每一个上面都要进行装饰

    用例级别的,每一个测试用户开始执行之前都需要执行用例的初始化,用例执行结束之后,执行用例的清除

    _______________这是setUpModule_____________
    _______________这是setUpClass_____________
    _______________这是setUp_____________
    ***********test_add************
    _______________这是tearDown_____________
    _______________这是setUp_____________
    ***********test_sub************
    _______________这是tearDown_____________
    _______________这是setUp_____________
    ***********test_func1************
    _______________这是tearDown_____________
    _______________这是setUp_____________
    ***********test_func2************
    _______________这是tearDown_____________
    _______________这是tearDownClass_____________
    _______________这是tearDownModule_____________
    
    import unittest
    from day04.calc import count
    '''
    unittest是python的单元测试框架,不需要安装直接导入使用即可
    测试用例类需要继承unittest.TestCase
    属性该类的测试用例其实就是一个实例方法
    该测试用例方法,必须要使用test开头
    '''
    # 模块的初始化
    def setUpModule():
        print("_______________这是setUpModule_____________")
    # 模块的清除
    def tearDownModule():
        print("_______________这是tearDownModule_____________")
    # 测试用例
    class CountTest(unittest.TestCase):
        # 类级别的初始化
        @classmethod
        def setUpClass(cls) -> None:
            print("_______________这是setUpClass_____________")
        # 类级别的清除
        @classmethod
        def tearDownClass(cls) -> None:
            print("_______________这是tearDownClass_____________")
        # 用例级别的初始化
        def setUp(self) -> None:
            print("_______________这是setUp_____________")
        def tearDown(self) -> None:
            print("_______________这是tearDown_____________")
        # 测试加法
        def test_add(self):
            print("***********test_add************")
            # 调用项目代码中的加法
            # 类的实例化
            c1 = count(5, 6)
            # 调用方法
            r = c1.add()
            # 测试的本质永远是实际结果和需求结果的比较
            # 断言
            self.assertNotEqual(r, 12)
        # 测试减法
        def test_sub(self):
            print("***********test_sub************")
            # 类的实例化
            c2 = count(6, 3)
            # 调用减法
            r = c2.Subtraction()
            # 断言
            self.assertEqual(r, 3)
        def test_func1(self):
            print("***********test_func1************")
            # 类的实例化
            c3 = count(23, 5)
            # false
            r = c3.func1()
            self.assertTrue(r, msg="测试不通过")
        def test_func2(self):
            print("***********test_func2************")
            # 类的实例化
            c4 = count(4,5)
            # 调用方法
            r = c4.func2()
            new_names = ["张三", '李四', '王五', "赵六", "张无忌", "周芷若", "赵敏"]
            # 取出某个人名,判断其在不在某个值中
            # self.assertIn(r, new_names)
            self.assertNotIn(r, new_names)
    if __name__ == '__main__':
        # 执行测试用例(执行该测试类下面的所有测试用例)
        CountTest.main()
    
unittest中为什么要用main()来启动所有的用例?

在 Python 的 unittest 框架中,main() 函数是一个特殊的入口点,它允许你启动测试套件并运行所有的测试用例。使用 main() 函数有几个好处:

  1. 统一的测试入口main() 提供了一个统一的入口点来运行所有的测试用例。这意味着你可以通过一个简单的命令来启动整个测试套件,而不需要在代码中显式地调用每个测试方法。
  2. 自动化和集成:使用 main() 函数可以方便地将测试集成到自动化构建和持续集成(CI)系统中。这些系统通常需要一个明确的入口点来运行测试。
  3. 测试发现unittest 框架提供了测试发现功能,它可以自动查找并运行所有以 test 开头的测试方法。main() 函数是这个发现过程的一部分。
  4. 测试套件的组织:通过使用 unittest.TestSuiteunittest.TestLoader,你可以将多个测试用例和测试套件组织在一起。main() 函数允许你将这些测试套件加载到测试运行器中,并统一运行。
  5. 命令行支持unittest 框架支持命令行运行,你可以通过命令行参数来控制测试的运行,如指定特定的测试用例、测试套件、测试发现的模式等。main() 函数处理这些命令行参数,并根据它们来运行测试。
  6. 测试结果的收集和报告main() 函数负责收集测试结果,并可以选择性地将结果输出到控制台或生成报告。这包括测试是否通过、失败的测试用例、错误信息等。
  7. 测试夹具的执行main() 函数还负责执行测试夹具(如 setUptearDown 方法),确保每个测试方法在适当的环境下运行。

五、Test Suite创建测试套件

测试套件就类似于测试单,测试用例是属于测试套件的,一个测试套件中可以有多条测试用例

1.添加单个用例到测试套件

import unittest
from day04.CountTest import CountTest
'''
通过执行器执行之后 一个点 就代表通过了一条测试用例
..F
'''
# 方法1:可以通过addTest()加载TestCase到TestSuite中。用于少量的测试用例
# 创建测试套件
suite = unittest.TestSuite()
# 给测试套件添加测试用例
suite.addTest(CountTest('test_add'))
suite.addTest(CountTest('test_sub'))
suite.addTest(CountTest('test_func1'))
# 创建执行器
runner = unittest.TextTestRunner()
# 使用上面创建的执行器,执行测试套件
runner.run(suite)

2.同时添加多个测试用例

#方法2:同时添加多个测试用例到套件之中
import unittest
from day04.CountTest import CountTest
# 创建测试套件
suite = unittest.TestSuite()
# 给测试套件添加测试用例
cases = [CountTest('test_func2'),CountTest('test_func1')]
# addTests可以接受一个列表
suite.addTests(cases)
# 生成执行器
runner = unittest.TextTestRunner()
runner.run(suite)

3.将某个测试类中的所有测试用例添加到测试套件中

# 方法3:添加整个类中的测试用例到套件中
import unittest
from day04.CountTest import CountTest
# 创建测试套件
suite = unittest.TestSuite()
# 给该测试类添加测试用例
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(CountTest))
# 创建测试执行器
runner = unittest.TextTestRunner()
# 执行套件
runner.run(suite)
unittest框架中的TestLoader类有什么作用,它如何帮助我组织测试用例?

unittest 框架中的 TestLoader 类是一个非常重要的组件,它负责发现和加载测试用例。TestLoader 类提供了一系列的功能,可以帮助你组织和管理测试用例,使得测试过程更加灵活和高效。以下是 TestLoader 类的一些主要作用:

  1. 发现测试用例
    • TestLoader 可以自动发现指定模块或包中的测试用例。它默认会查找所有继承自 unittest.TestCase 的类,以及这些类中所有以 test 开头的方法。
  2. 加载测试用例
    • 一旦找到测试用例,TestLoader 可以将它们加载到 unittest.TestSuite 对象中,这样就可以将多个测试用例组合在一起运行。
  3. 自定义测试发现
    • 你可以通过 TestLoader 的方法来自定义测试发现的行为,例如指定测试方法的命名规则、过滤不需要的测试用例等。
  4. 测试套件的组织
    • 使用 TestLoader,你可以将不同的测试用例和测试套件组织在一起,创建复杂的测试结构。
  5. 支持命令行参数
    • TestLoader 支持命令行参数,允许用户在命令行中指定要运行的测试用例或测试套件。
  6. 测试夹具的执行
    • TestLoader 负责在测试运行时调用测试夹具(如 setUptearDown 方法),确保测试环境的正确设置和清理。
  7. 测试结果的收集
    • TestLoader 可以与 unittest.TestResult 配合使用,收集测试结果并生成报告。
自定义测试发现
import unittest

class MyTestCase(unittest.TestCase):
    def test_one(self):
        self.assertEqual(1, 1)

    def not_a_test(self):
        pass

if __name__ == '__main__':
    # 创建 TestLoader 实例
    loader = unittest.TestLoader()
    # 设置测试方法的命名规则
    loader.testMethodPrefix = 'not_a'
    # 加载测试用例
    suite = loader.loadTestsFromTestCase(MyTestCase)
    # 运行测试套件
    runner = unittest.TextTestRunner()
    runner.run(suite)

在这个例子中,由于我们设置了 testMethodPrefixnot_a,所以只有 not_a_test 方法会被识别为测试方法。

TestLoader 支持哪些自定义测试发现的方法?

unittest.TestLoader 类提供了多种方法来自定义测试发现的过程,使得你可以更灵活地控制哪些测试用例被加载和执行。以下是一些常用的自定义测试发现的方法:

  1. loadTestsFromTestCase(testCaseClass, patternStart=None):
    • 这个方法用于从指定的 unittest.TestCase 子类中加载测试用例。你可以传递一个可选的 patternStart 参数来指定测试方法的命名规则。
  2. loadTestsFromTestCases(testCaseClasses, patternStart=None):
    • 类似于 loadTestsFromTestCase,但允许你传递一个 unittest.TestCase 子类的列表或元组,从而加载多个测试用例类。
  3. loadTestsFromModule(module):
    • 从指定的模块中加载测试用例。这个方法会查找模块中所有继承自 unittest.TestCase 的类,并加载它们的测试方法。
  4. loadTestsFromName(name, module=None):
    • 根据给定的名称模式加载测试用例。这个方法可以用于加载单个测试方法、测试类或测试套件。
  5. loadTestsFromNames(names, module=None):
    • 允许你传递一个名称列表,根据这些名称加载测试用例。
  6. getTestCaseNames(testCaseClass, patternStart=None):
    • 返回一个包含测试方法名称的列表。你可以传递一个 patternStart 参数来指定测试方法的命名规则。
  7. testMethodPrefix:
    • 这是一个属性,你可以设置它来定义测试方法的命名规则。默认情况下,unittest 会查找以 test 开头的方法。
  8. suiteClass:
    • 这是一个属性,你可以设置它来指定用于存储测试用例的测试套件类。默认是 unittest.TestSuite
  9. sortTestMethodsUsing:
    • 这是一个属性,你可以设置它来指定一个排序函数,用于在加载测试方法时对它们进行排序。
  10. topLevelDir:
    • 这是一个属性,用于指定测试发现的顶级目录。这在处理相对导入和模块路径时很有用。

通过这些方法和属性,你可以精细控制测试发现的过程,包括选择性地加载测试用例、自定义测试方法的命名规则、排序测试方法等。这使得 unittest 框架更加灵活和强大,能够适应不同的测试需求和场景。

4.模糊匹配

# 模糊匹配
import unittest
# 加载路径
casepath = "../day04"
# 指定匹配的规则,会自动生成测试套件,将用例添加进去
suite = unittest.defaultTestLoader.discover(start_dir=casepath, pattern='Cou*.py')
# 创建执行器
runner = unittest.TextTestRunner()
# 执行测试用例
runner.run(suite)

六、测试跳过

# @unittest.skip("该用例所对应的功能未开发完成")
# skipIf 如果为true则跳过不执行,
# @unittest.skipIf(5>4, "因为我要测试一下")
# 如果为true则报错,如果为false则跳过
# @unittest.skipUnless(3>4, "因为我要测试一下")
# 不论结果如何都是失败
# @unittest.expectedFailure("功能未能实现")
# 如果断言失败,不计入执行case数目中

七、生成测试报告

import unittest
import time
import os
from common.HTMLTestRunner import HTMLTestRunner
# 测试报告的存放路径
report_path = "./report/"
# 测试报告的标题
report_title = "冒烟测试"
# 测试的描述
report_desc = "本次测试是对于加减法功能的测试"
# 做一个判断,如果有了report目录直接使用,没有则创建
if not os.path.exists(report_path):
    # 没有则创建
    os.mkdir(report_path)
# 构建获取时间日期,作为报告的名字,避免重复
gettime = time.strftime("%Y%m%d%H%M%S")
# 构建测试报告存放路径和名字
filepath = report_path + f"report{gettime}.html"
# 执行测试,生成测试报告
# 执行加载用例的路径
case_path = "./test_case"
# 模糊匹配
suite = unittest.defaultTestLoader.discover(start_dir=case_path, pattern='C*.py')
with open(filepath, "wb") as f1:
    # 使用HTMLTestRunner
    # 第一个参数指的生成的测试报告给那个文件写入
    # 第一个参数是测试用例的标题
    # 第三个参数是对这一次测试的描述
    runner = HTMLTestRunner(f1, title=report_title, description=report_desc)
    # 执行测试套件
    runner.run(suite)
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值