• unittest框架


    1.通过unittest框架创建测试

    1.必须继承于unittest.TestCase类
    2.可以定义setUp和tearDown方法进行初始化,每条测试用例开始或结束会执行。也可以使用setUpClass和tearDownClass来进行初始化,整个程序开始或结束会执行。
    3.所有测试方法必须以test开头。测试方法会在运行时自动被调用。
    4.可以pycharm自带的unittest框架运行,Run ‘Unittest for…’。也可以以普通方式运行。
    在这里插入图片描述

    • setUp:每个测试方法执行前执行一次。
    • tearDown:每个测试方法结束后执行一次。
    • setUpClass:整个测试执行前执行一次。
    • tearDownClass: 整个测试执行后执行一次。

    2.通过unittest框架添加断言

    断言是为了判断实际结果与预期结果是否一致,任何测试都是需要断言的,否则就是操作,不能算自动化测试。在执行的过程中,断言失败了,就不会往下执行。
    常见断言

    1.assertIn

    assertIn(key, container, message)
    # key:在给定容器中检查其存在性的字符串
    # container:在其中搜索关键字符串的字符串
    # message:作为测试消息失败时显示的消息的字符串语句。可选项。
    
    • 1
    • 2
    • 3
    • 4

    如果 key 包含在container字符串中,它将返回true,否则返回false,并提示message。

    2.assertNotIn

    3.assertEqual

    assertEqual(firstValue, secondValue, message)
    # firstValue按函数比较中使用的任何类型的变量
    # secondValue:按函数比较时使用的任何类型的变量
    # message:作为测试消息失败时显示的消息的字符串语句。
    assertEqual(1, 1)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果两个输入值相等,则assertEqual()将返回true,否则返回false。

    4. assertNotEqual

    5.assertTrue

    assertTrue(testValue, message)
    # testValue:布尔类型的变量,用于按功能比较
    # message:作为测试消息失败时显示的消息的字符串语句。
    
    • 1
    • 2
    • 3

    如果测试值是true,则assertTrue()将返回true,否则返回false。

    6.assertFalse

    总结:assertIn和assertEqual比较常用

    3.自动化用例管理TestLoader类的用法

    测试用例有几百个,有时候只执行一部分,比如执行冒烟测试用例,回归测试用例,每次迭代的测试范围可能不一样。
    TestLoader类常见方法

    1.loadTestsFromTestCase()

    smoketests.py 冒烟测试用例

    import unittest
    class SmokeTests(unittest.TestCase):  # 测试类必须继承unittest.TestCase类
          # 测试方法命名必须以test开头
            def testSk1(self):  # 测试用例1
                self.assertIn('hello', 'hell world')
    
            def testSk2(self):  # 测试用例2
                self.assertNotIn('hello', 'world')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    requirementtests.py 需求测试用例

    import unittest
    class RequirementTests(unittest.TestCase):  # 测试类必须继承unittest.TestCase类
          # 测试方法命名必须以test开头
            def testRmt1(self):  # 测试用例1
                self.assertEqual(1, 1)
    
            def testRmt2(self):  # 测试用例2
                self.assertEqual(1, 1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    regressiontests.py 回归测试用例

    import unittest
    class RegressionTests(unittest.TestCase):  # 测试类必须继承unittest.TestCase类
          # 测试方法命名必须以test开头
            def testRg(self):  # 测试用例1
                self.assertIn(1, 1)
    
            def testRg(self):  # 测试用例2
                self.assertNotIn(1, 1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    performtests.py 执行用例

    from smoketests import SmokeTests
    from requirementtests import RequirementTests
    from regressiontests import RegressionTests
    import unittest
    if __name__ == '__main__':
        sk = unittest.defaultTestLoader.loadTestsFromTestCase(SmokeTests) # 执行冒烟测试类里面的测试用例
        rq = unittest.defaultTestLoader.loadTestsFromTestCase(RequirementTests) # 执行需求测试类里面的测试用例
        rg = unittest.defaultTestLoader.loadTestsFromTestCase(RegressionTests) # 执行回归测试类里面的测试用例
        suite = unittest.TestSuite([sk])  # 执行一个测试类
        suite = unittest.TestSuite([sk, rq, rg])  # 执行冒烟,需求,回归测试
        unittest.TextTestRunner.run(suite)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.loadTestsFromModule()

    从一个python文件里面导入测试方法
    performtests.py 执行用例

    import smoketests, requirementtests, regressiontests 
    import unittest
    if __name__ == '__main__':
        names = [smoketests, requirementtests, regressiontests]  # 存放需要执行的测试类
        modules = []
        for item in names:
        	module = unittest.defaultTestLoader.loadTestsFromModule(item)
        	modules.append(module)
        suite = unittest.TestSuite(modules)  
        unittest.TextTestRunner.run(suite)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.loadTestsFromNames()

    1)执行测试类里面的部分测试用例,意思是执行冒烟,需求,回归测试用例里面的几个测试用例
    performtests.py 执行用例

    from smoketests import SmokeTests
    from requirementtests import RequirementTests
    from regressiontests import RegressionTests
    import unittest
    if __name__ == '__main__':
    	names = ['smoketests.SmokeTests.testSk1', 'requirementtests.RequirementTests.testRmt1', 'regressiontests.RegressionTests.testRg1']  # 注意这里必须要从文件名.类名.方法写完整路径
    	perform_test = unittest.defaultTestLoader.loadTestsFromNames(names)
    	suite = unittest.TestSuite(perform_test) 
        unittest.TextTestRunner.run(suite)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2)如果想执行某个功能模块,这个模块在冒烟测试用例中有一些用例。
    smoketests.py 冒烟测试用例

    import unittest
    class SmokeTests(unittest.TestCase):  # 测试类必须继承unittest.TestCase类
          # 测试方法命名必须以test开头,同一个模块测试方法命名有相同的字段
          # 登录模块的测试用例
            def testLoginSk1(self):  # 测试用例1
                self.assertIn('hello', 'hell world1')
    		def testLoginSk2(self):  # 测试用例2
                self.assertIn('hello', 'hell world2')
            def testLoginSk3(self):  # 测试用例3
                self.assertIn('hello', 'hell world3')
          # 注册模块的测试用例
            def testregistSk1(self):  # 测试用例4
                self.assertNotIn('hello', 'world')
            def testregistSk2(self):  # 测试用例5
                self.assertNotIn('hello', 'world')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    performtests.py 执行用例

    from smoketests import SmokeTests
    import unittest
    def getFullTestCaseNames(names):
    	full_nams = []
    	for item in names:
    		if 'Login' in item:
    			full_names.append('smoketests.SmokeTests.' + item)
    if __name__ == '__main__':
    	names = unittest.defaultTestLoader.getTestCaseNames(SmokeTests)
    	perform_test = unittest.defaultTestLoader.loadTestsFromNames(getFullTestCaseNames(names))
    	suite = unittest.TestSuite(perform_test) 
        unittest.TextTestRunner.run(suite)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.discover() 最常用

    项目中最常用的方法,根据测试路径去选择执行哪些测试用例,冒烟,需求,回归测试用例用包分类存放用例
    cases包 存放测试文件smoke包 ,requirement包,regression包
    smoke包 存放冒烟测试用例的py文件
    requirement包 存放冒烟测试用例的py文件
    regression包 存放冒烟测试用例的py文件

    performtests.py 执行用例

    if __name__ == '__main__':
    	case_dir = 'cases'  # cases包
    	perform_test = unittest.defaultTestLoader.discover(case_dir + '/smoke', pattern='*.py')  # cases包下的smoke包下的所有py文件
    	suite = unittest.TestSuite(perform_test) 
        unittest.TextTestRunner.run(suite)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.unittest智能封装等待

    webdriver有强制等待,显示等待,隐式等待,详解见web自动化测试-等待

    import unittest
    from selenium import webdriver
    from selenium.common.exceptions import NoSuchFrameException
    from selenium.webdriver.support.wait import WebDriverWait
    
    class waitTest(unittest.TestCase):
    
        def setUp(self) -> None:
            self.driver = webdriver.Chrome()
            self.driver.get('')
    
        def tearDown(self) -> None:
            self.driver.quit()
    
        def find_element(self, locator):
        # 封装driver.find_element方法
        # locator参数是元素定位
            try:
                element = WebDriverWait(self.driver, 30 ,0.5).until(lambda x: x.find_element(*locator))
                return element
            except NoSuchFrameException as e:
                print('Error details:{}'.format(e.args[0])) # e.args[0]指错误的原因
                
        def test1(self):  # 测试用例1,这里元素操作就可以不用等待
            self.find_element(('id', 'username')).send_keys('admin')
            self.find_element(('id', 'password')).send_keys('123456')
            self.find_element(('id', 'login')).click()
            self.find_element(('partial link text', '会议'))
    
    • 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

    5.unittest数据驱动

    利用不同的测试数据来测试相同的场景,为了提高代码的重用性,增加代码效率而采用一种代码编写的方法,叫参数化,也就是数据驱动。
    比如登录操作,需要用正确账号密码登录,不正确账号密码登录,测试方法中的等价类方法,需要测试有效等价类,无效等价类。
    在unittest框架中使用参数化,需要安装ddt包。
    参数化的几种形式

    1.参数值为单个参数形式

    @data(1, 2, 3, 4)

    from selenium import webdriver
    import unittest
    from ddt import ddt, data, unpack
    
    
    @ddt  # 必须要在类前加上这个装饰器
    class LoginTest(unittest.TestCase):
    
        def setUp(self) -> None:
            self.driver = webdriver.Chrome()
            self.url = ''
    
        def tearDown(self) -> None:
            self.driver.quit()
        
        # 数据为单个值
        @data(1, 2, 3, 4)
        def test_data(self, value):
            print(value)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    结果输出:1 2 3 4

    2.参数值为组合参数形式

    @data(
    (‘’, ‘123456’, ‘9988’, ‘用户名或密码错误’),
    (‘admin’, ‘’, ‘9988’, ‘用户名或密码错误’),
    (‘system’, ‘123456’, ‘9988’, ‘用户名或密码错误’),
    (‘admin’, ‘7573890’, ‘9988’, ‘用户名或密码错误’),
    (‘admin’, ‘123456’, ‘gre5’, ‘验证码错误’)
    )
    @unpack

    logintest.py 登录功能

    from selenium import webdriver
    import unittest
    from ddt import ddt, data, unpack
    
    
    @ddt  # 必须要在类前加上这个装饰器
    class LoginTest(unittest.TestCase):
    
        def setUp(self) -> None:
            self.driver = webdriver.Chrome()
            self.url = ''
    
        def tearDown(self) -> None:
            self.driver.quit()
    
        # 登录成功的测试用例,类名以test开头
        def testLoginSuccess(self):
            self.driver.get(self.url)
            self.driver.find_element('id', 'username').send_keys('admin')  # 输入账号
            self.driver.find_element('id', 'username').send_keys('123456')  # 输入密码
            self.driver.find_element('id', 'verification').send_keys('9988')  # 输入验证码
            self.driver.find_element('xpath', '//input[@value="Login"]').click()  # 点击登录
            loginname = self.driver.find_element('id', 'loginName').text  # 登录进去显示账号名
            self.assertEqual('admin', loginname)  # 判断是否登录成功
    
        # 登录失败的测试数据组合
        @data(
            ('', '123456', '9988', '用户名或密码错误'),
            ('admin', '', '9988', '用户名或密码错误'),
            ('system', '123456', '9988', '用户名或密码错误'),
            ('admin', '7573890', '9988', '用户名或密码错误'),
            ('admin', '123456', 'gre5', '验证码错误')
        )
        @unpack  # 解包,将data里面的5种组合拆开成username, password, verification, errmsg参数传进下面函数中
        # 登录失败的测试用例,执行5次
        def testLoginFailed(self, username, password, verification, err):
            self.driver.get(self.url)
            self.driver.find_element('id', 'username').clear()
            self.driver.find_element('id', 'username').send_keys(username)  # 输入账号
            self.driver.find_element('id', 'username').clear()
            self.driver.find_element('id', 'username').send_keys(password)  # 输入密码
            self.driver.find_element('id', 'verification').clear()
            self.driver.find_element('id', 'verification').send_keys(verification)  # 输入验证码
            self.driver.find_element('xpath', '//input[@value="Login"]').click()  # 点击登录
            errmsg = self.driver.find_element('xpath', '//div[contains(text(), "{}")').format(err).text  # 登录失败显示的提示框
            self.assertEqual(errmsg, err)  # 判断是登录失败提示信息是否正确
    
    
    if __name__ == '__main__':
        test = unittest.defaultTestLoader.loadTestsFromTestCase(LoginTest)
        suite = unittest.TestSuite(test)
        unittest.TextTestRunner().run(suite)
    
    
    • 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

    缺点:如果数据比较多,写在代码里面,维护困难。

    3.从函数中返回参数值

    将测试数据单独放到一个函数中
    testdata.py

    class TestData:
        
        @classmethod
        def get_data(cls):
            testdata = [
                ('', '123456', '9988', '用户名或密码错误'),
                ('admin', '', '9988', '用户名或密码错误'),
                ('system', '123456', '9988', '用户名或密码错误'),
                ('admin', '7573890', '9988', '用户名或密码错误'),
                ('admin', '123456', 'gre5', '验证码错误')
            ]
            return testdata
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    logintest.py

    from selenium import webdriver
    import unittest
    from ddt import ddt, data, unpack
    from testdata import TestData
    
    @ddt  # 必须要在类前加上这个装饰器
    class LoginTest(unittest.TestCase):
    
        def setUp(self) -> None:
            self.driver = webdriver.Chrome()
            self.url = ''
    
        def tearDown(self) -> None:
            self.driver.quit()
    
        # 登录成功的测试用例,类名以test开头
        def testLoginSuccess(self):
            self.driver.get(self.url)
            self.driver.find_element('id', 'username').send_keys('admin')  # 输入账号
            self.driver.find_element('id', 'username').send_keys('123456')  # 输入密码
            self.driver.find_element('id', 'verification').send_keys('9988')  # 输入验证码
            self.driver.find_element('xpath', '//input[@value="Login"]').click()  # 点击登录
            loginname = self.driver.find_element('id', 'loginName').text  # 登录进去显示账号名
            self.assertEqual('admin', loginname)  # 判断是否登录成功
    
        # 登录失败的测试数据组合
        @data(*TestData.get_data())  # 这里的*号是解包的意思,将testdata列表解包出5个元组,然后穿给@data,如果不加*,就是将testdata列表值整个传给@data
        @unpack  # 解包,将data里面的5种组合拆开成username, password, verification, errmsg参数传进下面函数中
        # 登录失败的测试用例,执行5次
        def testLoginFailed(self, username, password, verification, err):
            self.driver.get(self.url)
            self.driver.find_element('id', 'username').clear()
            self.driver.find_element('id', 'username').send_keys(username)  # 输入账号
            self.driver.find_element('id', 'username').clear()
            self.driver.find_element('id', 'username').send_keys(password)  # 输入密码
            self.driver.find_element('id', 'verification').clear()
            self.driver.find_element('id', 'verification').send_keys(verification)  # 输入验证码
            self.driver.find_element('xpath', '//input[@value="Login"]').click()  # 点击登录
            errmsg = self.driver.find_element('xpath', '//div[contains(text(), "{}")').format(err).text  # 登录失败显示的提示框
            self.assertEqual(errmsg, err)  # 判断是登录失败提示信息是否正确
    
    • 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

    4. 从文件中返回参数值(最优,最常用)

    建一个文件存放测试数据

    data.txt

    ,123456,9988,用户名或密码错误
    admin,,9988,用户名或密码错误
    system,123456,9988,用户名或密码错误
    admin,7573890,9988,用户名或密码错误
    admin,123456,gre5,验证码错误
    
    • 1
    • 2
    • 3
    • 4
    • 5

    testdata.py 读取测试数据文件里面的数据的方法

    class TestData:
    
        @classmethod
        def get_data_from_file(cls, path):
            rows = []
            with open(path, 'r', encoding='utf-8') as f:
                for line in f:
                    user_data = line.strip().split(',')
                    rows.append(user_data)
            return rows
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    logintest.py

    from selenium import webdriver
    import unittest
    from ddt import ddt, data, unpack
    from testdata import TestData
    
    @ddt  # 必须要在类前加上这个装饰器
    class LoginTest(unittest.TestCase):
    
        def setUp(self) -> None:
            self.driver = webdriver.Chrome()
            self.url = ''
    
        def tearDown(self) -> None:
            self.driver.quit()
    
        # 登录成功的测试用例,类名以test开头
        def testLoginSuccess(self):
            self.driver.get(self.url)
            self.driver.find_element('id', 'username').send_keys('admin')  # 输入账号
            self.driver.find_element('id', 'username').send_keys('123456')  # 输入密码
            self.driver.find_element('id', 'verification').send_keys('9988')  # 输入验证码
            self.driver.find_element('xpath', '//input[@value="Login"]').click()  # 点击登录
            loginname = self.driver.find_element('id', 'loginName').text  # 登录进去显示账号名
            self.assertEqual('admin', loginname)  # 判断是否登录成功
    
        # 登录失败的测试数据组合
        @data(*TestData.get_data_from_file('data.txt'))  # 这里的*号是解包的意思,将testdata列表解包出5个元组,然后穿给@data,如果不加*,就是将testdata列表值整个传给@data
        @unpack  # 解包,将data里面的5种组合拆开成username, password, verification, errmsg参数传进下面函数中
        # 登录失败的测试用例,执行5次
        def testLoginFailed(self, username, password, verification, err):
            self.driver.get(self.url)
            self.driver.find_element('id', 'username').clear()
            self.driver.find_element('id', 'username').send_keys(username)  # 输入账号
            self.driver.find_element('id', 'username').clear()
            self.driver.find_element('id', 'username').send_keys(password)  # 输入密码
            self.driver.find_element('id', 'verification').clear()
            self.driver.find_element('id', 'verification').send_keys(verification)  # 输入验证码
            self.driver.find_element('xpath', '//input[@value="Login"]').click()  # 点击登录
            errmsg = self.driver.find_element('xpath', '//div[contains(text(), "{}")').format(err).text  # 登录失败显示的提示框
            self.assertEqual(errmsg, err)  # 判断是登录失败提示信息是否正确
    
    • 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

    6.断言失败截图

    当预期结果与实际结果不一样的时候,进行截图,能够更清晰的了解测试用例执行失败的情况。断言失败之后的代码就不执行了,所以在断言后面加截图程序是不可行的。

    1.通过异常处理的方式实现

    try except

    from selenium import webdriver
    import unittest
    
    
    class LoginTest(unittest.TestCase):
    
        def setUp(self) -> None:
            self.driver = webdriver.Chrome()
            self.url = ''
    
        def tearDown(self) -> None:
            self.driver.quit()
            
        def test_demo(self):
            try:
                self.assertEqual(1, 2)
            except AssertionError as e:
                import os, time
                day = time.strftime('%Y%m%d', time.localtime(time.time()))  # 获取当前时间
                screenshot_path = os.getcwd() + r'\reports\screenshot_%s' % day  # getcwd()表示获取项目的路径,该句是存放截图的位置和命名方式
                if not os.path.exists(screenshot_path):  # 如果文件路径不存在
                    os.makedirs(screenshot_path)  # 创建文件路径
    
                tm = time.strftime('%H%M%S', time.localtime(time.time()))
                self.driver.get_screenshot_as_file(screenshot_path + '\\{}_{}.png'.format('screen_shot', tm))
                raise e  # 将异常继续抛出给unittest,必须写
    
    
    if __name__ == '__main__':
        test = unittest.defaultTestLoader.loadTestsFromTestCase(LoginTest)
        suite = unittest.TestSuite(test)
        unittest.TextTestRunner().run(suite)
    
    • 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

    缺点:自动化测试项目中断言会很多,每个断言都写这么多代码比较麻烦。

    2.通过装饰器的方式实现(最常用)

    from selenium import webdriver
    import unittest
    
    
    class LoginTest(unittest.TestCase):
    
        def setUp(self) -> None:
            self.driver = webdriver.Chrome()
            self.url = ''
    
        def tearDown(self) -> None:
            self.driver.quit()
            
        # 装饰器,这是一个模板,其他也可以直接用
        def addpic(func):
            def wrapper(self, *args, **kwargs):
                try:
                    func(self, *args, **kwargs)
                except AssertionError as e:
                    import os, time
                    day = time.strftime('%Y%m%d', time.localtime(time.time()))  # 获取当前时间
                    screenshot_path = os.getcwd() + r'\reports\screenshot_%s' % day  # getcwd()表示获取项目的路径,该句是存放截图的位置和命名方式
                    if not os.path.exists(screenshot_path):  # 如果文件路径不存在
                        os.makedirs(screenshot_path)  # 创建文件路径
    
                    tm = time.strftime('%H%M%S', time.localtime(time.time()))
                    self.driver.get_screenshot_as_file(screenshot_path + '\\{}_{}.png'.format('screen_shot', tm))
                    raise e  # 将异常继续抛出给unittest,必须写
            return wrapper()
            
        @addpic  # 断言失败会生成截图,断言成功不会生成截图
        def test_demo(self):
            self.assertEqual(1, 2)
    
    • 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
  • 相关阅读:
    数理天地杂志数理天地杂志社数理天地编辑部2022年第20期目录
    eclipse进入断点之后,一直卡死,线程一直在运行【记录一种情况】
    微擎模块 志汇周边优惠卡券小程序5.2开源版 带最新小程序前端
    互联世界的桥梁:探索Facebook如何连接人与人之间的纽带
    设计模式四:适配器模式
    LeetCode 725. Split Linked List in Parts【链表】中等
    Excel 快速分析
    速卖通跨境智星靠谱吗?还有其他隐藏费用吗?
    15设计模式-行为型模式-观察者模式
    【C++进阶】多态
  • 原文地址:https://blog.csdn.net/XGZ2IT/article/details/127938726