Python学习3.0使用Unittest框架运行测试用例

使用Unittest框架运行测试用例

Unittest 框架

作用: 管理测试用例、断言、生成测试报告

1.Unittest 单元测试框架的组成

Unittest 组成部分 作用
test fixture 测试固定组件,unittest框架中,一些有固定用法的组件
test case 测试用例,被执行测试的最小单元
test suite 测试套件,它是一个用例集,用来汇总应该一起执行的测试用例
test runner 测试运行器,它是一个设计测试执行方式的元件,主要对用户提供了输出结果的展现方式。它可以用图标、文本、html等方式来展现测试结果

2.TestCase 编写测试用例

  • 简单执行一个用例 import unittest

Unittest运行的测试用例,必须是继承了 unittest.TestCase 的方法,并且这个方法默认以 test 开头

默认执行顺序是,在test以后,按照ASCII码比对大小

python 复制代码
---- 导入包 unittest
import unittest

----   创建继承  unittest.TestCase  的类
class FirstDemo(unittest.TestCase):   ------- 固定写法,必须继承
    def test01(self):
        print("执行用例一")

    def test02(self):
        print("执行用例二")

if __name__ == '__main__':
    unittest.main()      ----- 固定写法,调用底层逻辑main执行用例,查看下图
  • 执行结果(左侧为执行情况,右侧为执行输出和执行时间)
  • 若想更换执行时的测试运行程序

3.跳过要执行的测试用例

方法:使用装饰器 @unitest.skip("备注")

  • 案例
python 复制代码
---- 导入包 unittest
import unittest

----   创建继承  unittest.TestCase  的类
class FirstDemo(unittest.TestCase):   ------- 固定写法,必须继承
    def test01(self):
        print("执行用例一")
        
    @unittest.skip("跳过用例二")      ------ @unitest.skip
    def test02(self): 
        print("执行用例二")

if __name__ == '__main__':
    unittest.main()
  • 运行结果:跳过的内容会被标注

4.引入TestFixture

TestFixture一般都是写在运行的测试用例的类中,作用该类的类方法或者实例方法而存在

运行顺序:setUpClass() - - - 反复执行{ setUp()、测试用例test、tearDown() } - - - tearDownClass()|

TestFixture常用方法 应用场景
setUp() 每运行一个测试用例之前,先运行的函数。主要用于设置一些配置信息,静态属性等
setUpClass() 类方法,必须和@classmethod装饰器结合使用,实例化类后,会自动运行的方法主要用于实例化类、设置某些环境配置如数据库连接配置等
tearDown() 每运行完一个测试用例之后,后运行的函数。主要用于销毁每个测试用例之间的数据,释放资源,还原数据
tearDownClass() 类中的代码全部运行完成后,会自动运行的方法,必须和@classmethod装饰器结合使用。主要用于销毁类级别的资源,还原数据

@classmethod 装饰器

类方法:被 @classmethod 装饰的函数,可以不用实例化类,就能够访问呢

举例:class A: pass - - - 实例化: A().aa - - - 不实例化: A.bb

在 unittest 当中 @classmethod 是 setUpClass() 和 tearDownClass() 的固定写法

  • 案例:测试加法运算
python 复制代码
# 导入unittest模块
import unittest

# 创建要执行的测试类
class add_doing:
    def Calculator(self,x,y):
        return x + y

# 创建unittest的测试类
class TestPlus(unittest.TestCase):
    add = None

# 编写测试固件
    def setUp(self):   # 如果不想写内容可以写pass跳过
        self.x = 1
        self.y = 1
        print("运行了setUp")

    @classmethod
    def setUpClass(cls):
        cls.add = add_doing()
        print("运行了setUpClass")

    def tearDown(self):   # 和setUp一一对应
        del self.x
        del self.y
        print("运行了tearDown")

    @classmethod
    def tearDownClass(cls):   # 和setUpClass一一对应
        del cls.add
        print("运行了tearDownClass")

# 测试用例,测试要执行的测试类
    def test01(self):
        print(self.add.Calculator(2,4))

    def test02(self):
        print(self.add.Calculator(5,6))

    def test03(self):
        print(self.add.Calculator(self.x,self.y))

if __name__ == '__main__':
    unittest.main()
  • 运行结果

5.Unittest的断言

Unittest断言方法 作用
assertEqual(a,b) 检査 a和b是否相等
assertTrue(x) 检查x是不是一个True
assertls(a,b) 检查a和b是不是完全一样
assertlsNone(x) 检查x是不是一个None
assertIn(a,b) 检查a是不是b的子集
assertlsInstance(a,b) 检查a、b两个对象,实例类型是否相同
  • 案例
python 复制代码
import unittest

class TestAssert(unittest.TestCase):
----- unittest的断言是在TestCase中实现的,所以必须继承TestCase之后,才能够使用unittest的断言方法
    def setUp(self):
        self.l1,self.l2 = [1,2],[1,2]
        self.a = 1
        self.b = 1

    def test01_assertEqual(self):
    -----  断言self.a和self.b是否完全相等
        self.assertEqual(self.a, self.b)
        self.assertEqual(self.l1, self.l2)

    def test02_assertTrue(self):
    -----  assertTrue(x)可以根据传入的数据,判断真或者假(True,False)
    #  assertTrue(0)是False
    #  assertTrue(None)是False
    #  assertTrue(1)是True
        self.assertTrue(self.a)
        self.assertTrue(self.l1)

    def test03_assertIs(self):
    -----  断言self.a和self.b是否完全相同
        self.assertIs(self.a, self.b)
        self.assertIs(self.l1, self.l2)    # False

    def test04_assertIn(self):
    -----   断言a是否是 l1 的子集
        self.assertIn(self.a, self.l1)     # True
        self.assertIn(self.l1, self.l2)    ----- False  列表不能做子集运算

    def test05_assertIsNone(self):
    -----  断言是不是None
        self.assertIsNone(self.a)          # False
        self.assertIsNone(None)         # True

    def test06_assertIsInstance(self):
    -----   断言类型
        self.assertIsInstance(self.a, int)  # 判断a是不是整形  True
        self.assertIsInstance(self.l1, int) # False

6.TestSuite和TestRunner(不支持并发结果合并)

test suite 测试套件,它是一个用例集,用来汇总应该一起执行的测试用例
test runner 测试运行器,它是一个设计测试执行方式的元件,主要对用户的输出结果进行展示
  • 案例
python 复制代码
# 导包
import unittest

# 创建要执行的测试用例类
class TestSuiteDemo(unittest.TestCase):
    def test01(self):
        print("执行的测试用例test01,a")

    def test02(self):
        print("执行的测试用例test02,b")


class TestSuiteDemo2(unittest.TestCase):
    def test01(self):
        print("执行的测试用例test01,张三")

    def test02(self):
        print("执行的测试用例test02,李四")


if __name__ == '__main__':
# 实例化测试套件
    suite = unittest.TestSuite()

# 将测试用例添加到测试套件中
    suite.addTest(TestSuiteDemo('test01'))
    suite.addTest(TestSuiteDemo2('test02'))
# 实例化runner
    runner = unittest.TextTestRunner()

# 使用runner运行测试用例生成测试报告
    runner.run(suite)

1)运行时自动触发unittest.main()逻辑,需修改 PyCharm 配置

  • 修改配置后的运行结果

2)TestSuite添加测试用例的3种方式

TestSuite添加测试用例的3种方式 写法举例(括号内命名可变)
suite.addTest 单个添加 suite.addTest(TestSuiteDemo('test01'))
suite.addTests 多个添加 suie.addTests([TestSuiteDemo('test01'),TestSuiteDemo2('test02')])
TestLoader 下面会重点介绍

3)TestLoader添加测试用例

两种方法 解释
TestLoader().discover("./","*.py") 根据执行的路径来寻找指定py文件中的测试用例,加载到测试套件当中
TestLoader().loadTestsFromTestCase(测试用例类名) 根据测试用例的类名来加载测试用例
  • 案例
python 复制代码
# 导入模块
import unittest

# 创建测试用例的类
class TestSuiteDemo1(unittest.TestCase):
    def test01(self):
        print("执行的测试用例test01,a")

    def test02(self):
        print("执行的测试用例test02,b")


class TestSuiteDemo2(unittest.TestCase):
    def test01(self):
        print("执行的测试用例test01,张三")

    def test02(self):
        print("执行的测试用例test02,李四")



class TestSuiteDemo3(unittest.TestCase):
    def test01(self):
        print("单元测试")

    def test02(self):
        print("集成测试")

    def test03(self):
        print("系统测试")
  • 方法一:TestLoader().discover("./","*.py")
python 复制代码
if __name__ == '__main__':

    ------ 实例化testloaders
    tl = unittest.TestLoader()

    --- 使用testloader的对象tl来加载测试用例成测试套件
    suite = tl.discover('./','four_unittest.py')
    runner = unittest.TextTestRunner()
    runner.run(suite)
  • 运行结果
  • 方法二:TestLoader().loadTestsFromTestCase(测试用例类名)
python 复制代码
if __name__ == '__main__':
    # 实例化testloaders
    tl = unittest.TestLoader()

    # 使用testloader的对象tl来加载测试用例成测试套件
    suite = tl.loadTestsFromTestCase(TestSuiteDemo3)
    runner = unittest.TextTestRunner()
    runner.run(suite)
  • 运行结果

7. 生成测试报告TestResult

使用unittest运行测试用例,最后会生成测试报告,我们可以把测试报告保存到文件中

1)TextTestRunner生成的文本格式报告

  • 查看TextTestRunner底层代码
  • 案例
python 复制代码
if __name__ == '__main__':
    tl = unittest.TestLoader()
    suite = tl.discover('./','four_unittest.py')

    ---  使用TextTestRunner生成测试报告
    ---  1.打开要保存的测试报告文件,'w'是写入的意思
    with open('./result.txt',mode='w') as f:
    
    ---  2.实例化TextTestRunner
        runner = unittest.TextTestRunner(f, verbosity=3,descriptions=True)
        
    ---  3.使用runner运行测试套件
        runner.run(suite)
  • 运行结果

2)HTMLTestRunner_PY3生成的报告(好用,需安装)

HTMLTestRunner_Py3是一个能生成HTML格式的,显示更友好的测试报告,它不仅能够显示出测试用例的执行结果,还能跟踪测试用例执行失败的原因

HTMLTestRunner_PY3下载安装地址

  • 安装成功结果显示
  • 案例
python 复制代码
from Scripts.HTMLTestRunner import HTMLTestRunner

if __name__ == '__main__':
    tl = unittest.TestLoader()
    suite = tl.discover('./','four_unittest.py')

    ---  使用HTMLTestRunner生成测试报告
    
    ---  1.打开要保存的测试报告文件,'wb'是写入的意思
    with open('./result.html',mode='wb') as f:
    
    ---  2.实例化HTMLTestRunner
        runner = HTMLTestRunner(f,verbosity=2,title='演示HTML测试报告')
        
    ---  3.使用runner运行测试套件
        runner.run(suite)
  • 运行结果
  • 浏览器展示

8. 框架进行并发(unittest不适合,使用pytest)

使用多线程运行,可以并行执行测试用例,提升测试速度

框架 是否推荐多线程 推荐方式
unittest 不推荐手动用 threading 或 threadpool 使用 concurrent.futures + 手动结果汇总,或改用 pytest
pytest 强烈推荐使用并发执行 使用 pytest-xdist 插件(基于多进程,非多线程)
  • unittest使用concurrent.futures多线程举例(适用于老项目)
python 复制代码
from concurrent.futures import ThreadPoolExecutor
import unittest


def run_test_case(test):
    result = unittest.TestResult()
    test(result)
    return result


if __name__ == '__main__':
    # 拆分测试用例
    suite = unittest.TestLoader().discover('./','*.py')
    test_cases = [test for test in suite]

    with ThreadPoolExecutor(max_workers=4) as executor:
        futures = [executor.submit(run_test_case, test) for test in test_cases]

        # 汇总结果
        total_result = unittest.TestResult()
        for future in futures:
            result = future.result()
            # 手动合并结果(注意线程安全)
            total_result.errors.extend(result.errors)
            total_result.failures.extend(result.failures)
            total_result.testsRun += result.testsRun
相关推荐
DKPT3 小时前
JVM新生代和老生代比例如何设置?
java·开发语言·jvm·笔记·学习
Jayyih4 小时前
嵌入式系统学习Day30(udp)
网络协议·学习·udp
豆浩宇4 小时前
学习PaddlePaddle--环境配置-Windows 11 + RTX 4060
人工智能·windows·深度学习·学习·目标检测·计算机视觉·paddlepaddle
鲸落落丶4 小时前
finalize() 方法介绍
开发语言·python
charlie1145141914 小时前
Windows 编程——字符串处理
windows·学习·c·字符串处理·windows编程
HappyAcmen4 小时前
【自动化实战】Python操作Excel/WORD/PDF:openpyxl与docx库详解
python·自动化·excel
q567315234 小时前
无需Python:Shell脚本如何成为你的自动化爬虫引擎?
开发语言·爬虫·python·自动化·scala
aiden:)4 小时前
App UI 自动化环境搭建指南
运维·python·ui·appium·自动化
一杯敬朝阳 一杯敬月光6 小时前
记录下chatgpt的openai 开发过程
python·chatgpt·flask