• 【07节】Python3+Selenium4自动化 unittest 测试框架详解


    1、unittest 框架介绍

    做过自动化测试的同学应该都知道python中的unittest框架,它是python自带的一套测试框架,学习起来也相对较容易,unittest框架最核心的四个概念:

    • test case:就是我们的测试用例,unittest中提供了一个基本类TestCase,可以用来创建新的测试用例,一个TestCase的实例就是一个测试用例;unittest中测试用例方法都是以test开头的,且执行顺序会按照方法名的ASCII值排序。

    • test fixure:测试夹具,用于测试用例环境的搭建和销毁。即用例测试前准备环境的搭建(SetUp前置条件),测试后环境的还原(TearDown后置条件),比如测试前需要登录获取token等就是测试用例需要的环境,运行完后执行下一个用例前需要还原环境,以免影响下一条用例的测试结果。

    • ​test suite:测试套件,用来把需要一起执行的测试用例集中放到一块执行,相当于一个篮子。我们可以使用TestLoader来加载测试用例到测试套件中。

    • test runner:用来执行测试用例的,并返回测试用例的执行结果。它还可以用图形或者文本接口,把返回的测试结果更形象的展现出来,如:HTMLTestRunner。

    2、创建单元测试步骤

    测试结果三种状态:测试通过 、测试失败、测试错误。

    # 步骤1 - 在程序中导入unittest模块。
    import unittest
    
    # 步骤2 - 定义要测试的功能。在下面的例子中,add()函数要进行测试。
    def add(x, y):
        return x + y
        
    # 步骤3 - 通过对unittest.TestCase进行子类化创建测试用例。
    class test_simple(unittest.TestCase):
        # 步骤4 - 将测试定义为类中的方法。方法名称必须以"test"开头。
        def testadd(self):
            # 步骤5 - 每个测试调用TestCase类的断言功能。有许多类型的断言。以下示例调用assertEquals()函数。
            # 步骤6 - assertEquals()函数将add()函数与arg2参数的结果进行比较,如果比较失败,则抛出assertionError。
            self.assertEqual(add(4, 6), 10)
            
    # 步骤7 - 最后,从unittest模块调用main()方法。
    if __name__ == '__main__':
        unittest.main()
    
    # 步骤8 - 从命令行运行上述脚本。
    # 步骤9 - 以下三个可能是测试的可能结果。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3、unittest 模块介绍

    在 unittest 模块中定义的类和方法。该模块有五个主要类。

    3.1 TestCase 类

    此类的对象表示最小的可测试单元。它保存测试例程,并提供钩子来准备每个例程并在此之后进行清理。

    3.1.1 TestCase 类常用方法:

    . setUp():调用准备测试夹具的方法。在调用测试方法之前立即调用该方法。

    tearDown():调用测试方法并记录结果后立即调用的方法。

    setUpClass():在一个类运行中调用测试之前的类方法。 setUpClass以类作为唯一参数调用,并且必须修饰为classmethod()。在方法上使用@classmethod装饰器装饰。

    tearDownClass():在单个类中的测试运行后调用的类方法。 tearDownClass以类作为唯一参数调用,并且必须修饰为classmethod()。在方法上使用@classmethod装饰器装饰。

    run(result = None):运行测试,将结果收集到TestResult作为result传递的对象中。如果省略result或None,则将创建一个临时结果对象(通过调用该defaultTestResult() 方法)并将其使用。结果对象返回给run()的调用者。

    skipTest(reason):在测试方法期间调用此方法,reason输入原因。

    subTest(msg=None,**params):返回一个上下文管理器,该上下文管理器将附带的代码块作为子测试执行。 msg和params是可选的,msg可以为任意值,在子测试失败时会显示这些值,使您可以清楚地识别它们。

    debug():运行测试而不收集结果。这样可以将测试引发的异常传播到调用方,并可以用来支持在调试器下运行测试。

    3.1.2 TestCase 类其他方法:

    """
    fail(msg=None):指定断言失败的错误信息msg
    """
        def fail(self, msg="test fail ha ha ha"):
            print("用例执行错误信息:{}".format(msg))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    """
    id():获取测试方法的全名,包括模块和类名。
    """
        def test_split(self):
            print(self.id())
            print("this is a test_split method")
            s = 'hello world'
            self.assertEqual(s.split(), ['hello', 'world'])
            # check that s.split fails when the separator is not a string
            with self.assertRaises(TypeError):
                s.split(2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    """
    defaultTestResult():返回应用于此测试用例类的测试结果类的实例(如果未向该run()方法提供其他结果实例 )。
    shortDescription():返回测试的描述,或者None没有提供描述。此方法的默认实现返回测试方法doc string的第一行(如果有)。
    """
        def test_isupper(self):
            """
            这是一个描述
            :return:
            """
            print("this is a test_isupper method")
            print(self.shortDescription())
            self.assertTrue('FOO'.isupper())
            self.assertFalse('Foo'.isupper())
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3.2 setUp() 与 tearDown()

    setUp() 与 tearDown() 方法在测试运行前做一些操作,测试过程会实例化浏览器、获取URL、设置等操作放在 setUp() 中;测试完成后的清除工作、关闭浏览器、数据库等放在 tearDown()中。

    import unittest
    
    class Test_Demo(unittest.TestCase):
        def setUp(self):
            print("开始执行测试用例啦!!!")
    
        def test_001(self):
            self.assertEqual((1 + 1 == 2), True)
            print("第一条用例")
    
        def test_002(self):
            self.assertEqual((2 + 3 ==5), True)
            print("第二条用例")
    
        def tearDown(self):
            print("每条用例执行完成显示!!")
    
    if __name__ == '__main__':
        unittest.main()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3.3 setUpClass() 与 tearDownClass()

    TestCase类有一个setUpClass()方法,可以在执行TestCase类中的单个测试之前被覆盖执行。类似地,tearDownClass()方法将在类中的所有测试之后执行。这两种方法都是类方法。因此,它们必须使用@classmethod指令进行装饰。

    import unittest
    from selenium import webdriver
    from time import sleep
    from selenium.webdriver.common.by import By
    
    class TestDemo2(unittest.TestCase):
        def setUp(self):
            print("开始执行测试用例啦!!!")
    
        @classmethod
        def setUpClass(cls) -> None:
            cls.driver = webdriver.Chrome()
            cls.driver.maximize_window()
    
        def test_xl(self):
            sleep(3)
            self.driver.get("https://mail.sina.com.cn/")
            self.driver.find_element(By.ID, 'freename').send_keys('kasthsoft@sina.com')
            self.driver.find_element(By.ID, 'freepassword').send_keys('xxxxx')
            self.driver.find_element(By.LINK_TEXT, '登录').click()
    
        def test_baidu(self):
            self.driver.get("http://www.baidu.com/")
            self.driver.find_element(By.ID, 'kw').send_keys("Python Web自动化测试")
            self.driver.find_element(By.ID, 'su').click()
    
        @classmethod
        def tearDownClass(cls) -> None:
            sleep(3)
            # driver.close()关闭驱动,driver.quit()退出驱动,开发中一般建议使用driver.quit()
            cls.driver.quit()
    
        def tearDown(self) -> None:
            print("每条测试执行完成啦!!")
    
    if __name__ == '__main__':
        unittest.main()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    3.3 TestSuite 类

    Python的测试框架提供了一个有用的机制,通过这些机制可以根据测试用例的特征将测试用例实例分组在一起。该机制由Unittest模块中的TestSuite类提供。

    import unittest
    
    # 步骤1 - 创建一个TestSuite类的实例。
    suite = unittest.TestSuite()
    
    # 步骤2 - 在套件中的TestCase类中添加测试。
    suite.addTest()
    
    # 步骤3 - 您还可以使用makeSuite()方法从类添加测试
    suite = unittest.makeSuite()
    
    # 步骤4 - 还可以在套件中添加个别测试。
    suite.addTest('测试方法'("测试名称"))
    
    # 步骤5 - 创建TestTestRunner类的对象。
    runner = unittest.TextTestRunner()
    
    # 步骤6 - 调用run()方法来运行套件中的所有测试
    runner.run(suite)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.4 TestLoader 类

    unittest 包具有 TestLoader 类,用于从类和模块创建测试套件。默认情况下,当unittest.main(0方法被调用)时,会自动创建unittest.defaultTestLoader 实例。然而,显式实例可以自定义某些属性。

    • loadTestsFromTestCase() 返回TestCase类中包含的所有测试用例的一套。
    • loadTestsFromModule() 返回给定模块中包含的所有测试用例的一套。
    • loadTestsFromName() 给一个字符string说明符返回一套所有测试用例。
    • loadTestsFromNames() 返回在testCaseClass中找到的方法名称的排序。

    3.5 TestResult 类

    此类用于编译有关已成功测试的测试和遇到故障的测试。TestResult 对象存储一组测试的结果。TestResult 实例由TestRunner.run()方法返回。

    import unittest
    from selenium import webdriver
    from time import sleep
    from selenium.webdriver.common.by import By
    
    class TestDemo2(unittest.TestCase):
        def setUp(self):
            print("开始执行测试用例啦!!!")
    
        @classmethod
        def setUpClass(cls) -> None:
            cls.driver = webdriver.Chrome()
            cls.driver.maximize_window()
    
        def test_xl(self):
            sleep(3)
            self.driver.get("https://mail.sina.com.cn/")
            self.driver.find_element(By.ID, 'freename').send_keys('kasthsoft@sina.com')
            self.driver.find_element(By.ID, 'freepassword').send_keys('xxxxx')
            self.driver.find_element(By.LINK_TEXT, '登录').click()
    
        def test_baidu(self):
            self.driver.get("http://www.baidu.com/")
            self.driver.find_element(By.ID, 'kw').send_keys("Python Web自动化测试")
            self.driver.find_element(By.ID, 'su').click()
    
        @classmethod
        def tearDownClass(cls) -> None:
            sleep(3)
            # driver.close()关闭驱动,driver.quit()退出驱动,开发中一般建议使用driver.quit()
            cls.driver.quit()
    
        def tearDown(self) -> None:
            print("每条测试执行完成啦!!")
    
    
    if __name__ == '__main__':
        unittest.main()
        runner = unittest.TextTestRunner()
        test_suite = TestDemo2()
        # TestResult实例由TestRunner.run()方法返回
        result = runner.run(test_suite)
        # 包含TestCase实例和持有格式化回溯的字符string的2元组的列表。每个元组代表一个引发意外异常的测试。
        print(result.errors)
        # 包含TestCase实例和持有格式化回溯的字符string的2元组的列表。每个元组表示使用TestCase.assert *()方法明确发出故障的测试。
        print(result.failures)
        # 包含TestCase实例和两个元组的列表,包含跳过测试的原因。
        print(result.skipped)
        # 如果所有测试运行到目前为止,返回True,否则返回False。
        print(result.wasSuccessful())
        # 可以调用此方法来表明正在运行的一组测试应该被中止。
        print(result.stop())
        # 在执行任何测试之前调用一次。
        print(result.startTestRun())
        # 所有测试执行一次后调用。
        print(result.stopTestRun())
        # 到目前为止的测试总数。
        print(result.testsRun)
        # 如果设置为true,sys.stdout和sys.stderr将在被调用的startTest()和stopTest()之间缓冲。
        print(result.buffer)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    4 、unittest 常用断言方法

    ​ 在python基础中,我们有讲过一个assert断言,使用方法比较简单,即assert 表达式, 提示信息,而unittest框架中也提供了一个自带的断言方式,主要有以下几种:

    方法检查说明
    assertEqual(arg1,arg2,msg = None)a ==b测试arg1和arg2是否相等。如果值不相等,测试将失。
    assertNotEqual(arg1,arg2,msg = None)a !=b测试arg1和arg2不相等。如果值相等,测试将失败
    assertTrue(expr,msg = None)bool(x) is True测试expr是真的。如果为false,则测试失败
    assertFalse(expr,msg =无)Bool(x) is False测试expr是false。如果为true,则测试失败
    assertIs(arg1,arg2,msg =无)a is b测试arg1和arg2评估到同一个对象
    assertIsNot(arg1,arg2,msg =无)a is not b测试arg1和arg2不评估相同的对象
    assertIsNone(expr,msg =无)x is None测试expr是None。如果不是无,测试失败
    assertIsNotNone(expr,msg = None)x is not None测试expr不是None。如果无,测试失败
    assertIn(arg1,arg2,msg =无)a in b测试arg1在arg2中
    assertNotIn(arg1,arg2,msg =无)a not in b测试arg1不在arg2中
    assertIsInstance(obj,cls,msg = None)isinstance(a,b)测试obj是cls的一个实例
    assertNotIsInstance(obj,cls,msg = None)not isinstance(a,b)测试obj不是cls的实例
    import unittest
    
    class TestDemo(unittest.TestCase):
       def test1(self):
          self.assertEqual(4 + 5,9)
       def test2(self):
          self.assertNotEqual(5 * 2,10)
       def test3(self):
          self.assertTrue(4 + 5 == 9,"The result is False")
       def test4(self):
          self.assertTrue(4 + 5 == 10,"assertion fails")
       def test5(self):
          self.assertIn(3,[1,2,3])
       def test6(self):
          self.assertNotIn(3, range(5))
    
    if __name__ == "__main__":
       unittest.main()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    5、unittest 项目实战分解

    5.1 TestCase 测试用例

    编写测试用例前,我们需要建一个测试类继承unittest里面的TestCase类,继承这个类之后我们才是真正的使用unittest框架去写测试用例,编写测试用例的步骤如下:

    • 导入unittest模块
    • 创建一个测试类,并继承 unittest.TestCase()
    • 定义测试方法,方法名必须以test_开头
    • 调用 unittest.main() 方法来运行测试用例,unittest.main() 方法会搜索该模块下所有以 test 开头的测试用例方法,并自动执行

    5.2 TestFixure 测试夹具

    ​ unittest的测试夹具有两种使用方式,一种是以测试方法为维度的setUp()和tearDown(),一种是以测试类为维度的setUpClass()和tearDownClass()。

    代码:5.1 与 5.2 内容代码演示

    import unittest
    from selenium import webdriver
    from time import sleep
    from selenium.webdriver.common.by import By
    
    class TestDemo2(unittest.TestCase):
        def setUp(self):
            print("开始执行测试用例啦!!!")
    
        @classmethod
        def setUpClass(cls) -> None:
            cls.driver = webdriver.Chrome()
            cls.driver.maximize_window()
    
        def test_xl(self):
            sleep(3)
            self.driver.get("https://mail.sina.com.cn/")
            self.driver.find_element(By.ID, 'freename').send_keys('kasthsoft@sina.com')
            self.driver.find_element(By.ID, 'freepassword').send_keys('xxxxx')
            self.driver.find_element(By.LINK_TEXT, '登录').click()
    
        def test_baidu(self):
            self.driver.get("http://www.baidu.com/")
            self.driver.find_element(By.ID, 'kw').send_keys("Python Web自动化测试")
            self.driver.find_element(By.ID, 'su').click()
    
        @classmethod
        def tearDownClass(cls) -> None:
            sleep(3)
            # driver.close()关闭驱动,driver.quit()退出驱动,开发中一般建议使用driver.quit()
            cls.driver.quit()
    
        def tearDown(self) -> None:
            print("每条测试执行完成啦!!")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    5.3 TestSuite 测试套件

    unittest.TestSuite()类来表示一个测试用例集,把需要执行的用例类或模块存到一起。

    1. unittest.TestSuite()
      addTest():添加单个测试用例方法
      addTest([…]):添加多个测试用例方法,方法名存在一个列表

    2. unittest.TestLoader()
      loadTestsFromTestCase(测试类名):添加一个测试类
      loadTestsFromModule(模块名):添加一个模块
      discover(测试用例的所在目录):指定目录去加载,会自动寻找这个目录下所有符合命名规则的测试用例

    # run_test.py,与TsetDemo1.py、TsetDemo2.py同一目录下
    import unittest
    import TsetDemo2 # 导入 TsetDemo2 方法
    
    # 第一步,创建一个测试套件
    suite = unittest.TestSuite()
    
    # 第二步:将测试用例,加载到测试套件中
    # 方式1,添加单条测试用例
    # 创建一个用例对象,注意:通过用例类去创建测试用例对象的时候,需要传入用例的方法名(字符串类型)
    case = test_register.TestRegister("用例")
    # 添加用例到测试套件中	
    suite.addTest(case)
    
    # 方式2,添加多条测试用例
    case1 = test_register.TestRegister("用例1")
    case2 = test_register.TestRegister("用例2")
    # 添加用例到测试套件中
    suite.addTest([case1, case2])	
    
    # 方式3,添加一个测试用例类
    # 创建一个加载对象
    loader = unittest.TestLoader()
    suite.addTest(loader.loadTestsFromTestCase(test_register.TestRegister))
    
    # 方式4,添加一个模块
    # 创建一个加载对象
    loader = unittest.TestLoader()	
    suite.addTest(loader.loadTestsFromModule(test_register))
    
    # 方式5,指定测试用例的所在的目录路径,进行加载
    # 创建一个加载对象
    loader = unittest.TestLoader()
    suite.addTest(loader.discover(r"目录路径"))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    ​ 通常使用方式 4、5比较多,你可以根据实际情况来运用。其中方式5,还可以自定义匹配规则,默认是会寻找目录下**test*.py文件,即所有以test开头命名的py文件。

    # 方式5,指定测试用例的所在的目录路径,进行加载
    # 创建一个加载对象
    loader = unittest.TestLoader()
    # 匹配规则:所有以test_case开头的
    suite.addTest(loader.discover(r"目录路径" , pattern="test_case*.py"))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5.4 TestRunner 执行用例

    ​ testrunner顾名思义就是用来执行测试用例的,并且可以生成相应的测试报告。测试报告有两种展示形式,一种是text文本,一种是html格式。

    # run_test.py,与TsetDemo1.py、TsetDemo2.py同一目录下
    import unittest
    import TsetDemo2 # 导入 TsetDemo2 方法
    from HTMLTestRunner import HTMLTestRunner   # 请跳转目录 6、Unittest 生成 HTML 测试报告
    
    # 创建测试套件
    suite = unittest.TestSuite()
    
    # 通过模块加载测试用例
    loader = unittest.TestLoader()
    suite.addTest(loader.loadTestsFromModule(test_register))
    
    # 创建测试运行程序启动器
    runner = HTMLTestRunner(stream=open("report.html", "wb"),  # 打开一个报告文件,将句柄传给stream
                            tester="DemoYi",                     # 报告中显示的测试人员
                            description="注册接口测试报告",     # 报告中显示的描述信息
                            title="自动化测试报告")             # 报告的标题
    
    # 使用启动器去执行测试套件里的用例
    runner.run(suite)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    6、Unittest 生成 HTML 测试报告

    ​ html格式的就是HTMLTestRunner了,HTMLTestRunner是 Python 标准库的 unittest 框架的一个扩展,它可以生成一个直观清晰的 HTML 测试报告。使用的前提就是要下载 HTMLTestRunner.py,下载完后放在python的安装目录下的scripts目录下即可。

    6.1 HTMLTestRunner 相关参数

    1. stream:指定输出的方式

    2. tester:报告中要显示的测试人员的名字

    3. description:报告中要显示的面熟信息

    4. title:测试报告的标题

    5. verbosity :表示测试报告信息的详细程度,一共三个值,默认是2

      0 (静默模式):你只能获得总的测试用例数和总的结果,如:总共100个 失败10 成功90
      1 (默认模式):类似静默模式,只是在每个成功的用例前面有个. 每个失败的用例前面有个F
      2 (详细模式):测试结果会显示每个测试用例的所有相关的信息
      
      • 1
      • 2
      • 3

    6.2 HTMLTestRunner 下载

    下载地址:HTMLTestRunnerCN 版本已经进行深度优化过后的中文版本。放心下载使用。

  • 相关阅读:
    MySQL数据库 || 增删改查操作详解
    RDS - 远程桌面服务
    LangChain转换链:让数据处理更精准
    【大厂AI课学习笔记NO.79】机器学习行业人才能力图谱
    1162地图分析
    React脚手架中配置代理的两种方式
    IIS方式部署项目发布上线
    kafka知识总结
    大一新生HTML期末作业 学生个人网页设计作业 HTML5响应式个人简历网站模板 web前端网页制作课作业
    PMP备考大全:经典题库(6月第4周)
  • 原文地址:https://blog.csdn.net/KasthSoft/article/details/128001342