python单元测试unittest学习

目录

为什么做单元测试

1.单元测试:对于软件设计最小的单元进行正确性测试,将可能存在的错误在最小范围内解决。
2.目的:发现模块内部逻辑,语法,算法,功能等错误。
3.测试方式:代码级别的测试和模块功能的测试
3.1代码级别测试
根据内部逻辑与接口编写测试用例
接口测试:确保接口实现正确,符合设计文档规范
数据结构测试:确保数据接口可用,例如数据库,文件,自定义数据结构;
边界测试:对于边界进行测试

单元测试用例编写

基础测试用例

def add(*args):
    result = 0
    for i in args:
        result +=i
    return result

print(add(1))
print(add(1,2))
print(add(1,"a"))
...
1
3
Traceback (most recent call last):
  File "C:\Users\shitou\eclipse-workspace\python_test\src\test\__init__.py", line 9, in <module>
    print(add(1,"a"))
  File "C:\Users\shitou\eclipse-workspace\python_test\src\test\__init__.py", line 4, in add
    result +=i
TypeError: unsupported operand type(s) for +=: 'int' and 'str'

unittest单元测试框架

1.测试用例:
1).测试环境准备
2).测试代码执行
3).测试环境还原
4).测试回归化
5).测试结果分析

2.测试用例结构

import unittest

class MyTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
    #初始化测试:构建预置条件,套件执行一次
    
    def setUp(self):
    #初始化:构建预置条件,每个脚本执行一次
    
    def tearDown(self):
    #恢复测试环境:清理测试数据等,每个脚本执行一次
    
    @classmethod
    def tearDownClass(cls):
    #恢复测试环境:清理测试数据等,套件执行一次
    def test_xxx(self):
        #测试用例必须以test开头
        
if __name__=="__main__":
    unittest.main()#执行测试用例

3.实例测试
1)待测方法

def add(a,b):
    return a+b

2)测试用例

import unittest
from test import myfunction as MF

class MyTest(unittest.TestCase):

    def setUp(self):
        print("test start")
    
    def tearDown(self):
        print("end test")

    def test_001(self):
        result = MF.add(1, 2)
        self.assertEqual(result, 3)
        
    def test_002(self):
        result = MF.add(0, 2)
        self.assertEqual(result, 2)

    def test_003(self):
        self.assertRaises(TypeError, MF.add(0, 2))
        
if __name__=="__main__":
    unittest.main()
    

测试集unittest.TestSuite

import unittest
from test import myfunction as MF

class MyTest(unittest.TestCase):

    def setUp(self):
        pass
    
    def tearDown(self):
        pass

    def test_001(self):
        result = MF.add(1, 2)
        self.assertEqual(result, 3)
        
    def test_002(self):
        result = MF.add(0, 2)
        self.assertEqual(result, 2)

    def test_003(self):
        self.assertRaises(TypeError, MF.add(0, 2))
        
if __name__=="__main__":
    suite = unittest.TestSuite()
    #添加单个测试用例
    suite.addTest(MyTest("test_001"))
    suite.addTest(MyTest("test_002"))
    #添加多个测试用例
    test_cases = [MyTest("test_003")]
    suite.addTests(test_cases)
    #统计测试用例数
    tc_num = suite.countTestCases()
    print(tc_num) #3
    runner = unittest.TextTestRunner(verbosity=0)
    runner.run(suite)  #执行
#0 (静默模式): 你只能获得总的测试用例数和总的结果 比如 总共100个 失败20 成功80
3
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

#1 (默认模式): 非常类似静默模式 只是在每个成功的用例前面有个“.” 每个失败的用例前面有个 “F”
3
.F.
#2 (详细模式):测试结果会显示每个测试用例的所有相关的信息
并且 你在命令行里加入不同的参数可以起到一样的效果
3
test_001 (__main__.MyTest) ... ok
test_002 (__main__.MyTest) ... ok
test_003 (__main__.MyTest) ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

断言

        result = MF.add(1, 2)
        self.assertEqual(result, 4,"预期是%d,实际是%d")
        self.assertTrue(expr, msg)
        ...

跳过测试用例

1.使用装饰器unittest.skip

import unittest
from test import myfunction as MF

class MyTest(unittest.TestCase):

    def setUp(self):
        pass
    
    def tearDown(self):
        pass

    def test_001(self):
        result = MF.add(1, 2)
        self.assertEqual(result, 3,"预期是%d,实际是%d")
    @unittest.skip("Not Test")
    def test_002(self):
        print("no test")
        result = MF.add(0, 2)
        self.assertEqual(result, 3)
#   
#     def test_003(self):
#         self.assertRaises(TypeError, MF.add(0, 2))
#         
if __name__=="__main__":
    unittest.main()
# .
# ----------------------------------------------------------------------
# Ran 2 tests in 0.000s
# 
# OK (skipped=1)

2.skipIf
测试方法

version = 1
def add(a,b):
    return a+b

测试用例

import unittest
from test import myfunction as MF

class MyTest(unittest.TestCase):
    def setUp(self):
        pass
    
    def tearDown(self):
        pass

    def test_001(self):
        result = MF.add(1, 2)
        self.assertEqual(result, 3,"预期是%d,实际是%d")
    @unittest.skipIf(MF.version==1, "跳过测试")     #满足条件则不执行
    def test_002(self):
        result = MF.add(0, 2)
        self.assertEqual(result, 3)
.
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK (skipped=1)

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

3.skipUnless

import unittest
from test import myfunction as MF

class MyTest(unittest.TestCase):
    def setUp(self):
        pass
    
    def tearDown(self):
        pass

    def test_001(self):
        result = MF.add(1, 2)
        self.assertEqual(result, 3,"预期是%d,实际是%d")
    @unittest.skipIf(MF.version==1, "跳过测试")     #满足条件则跳过
    def test_002(self):
        result = MF.add(0, 2)
        self.assertEqual(result, 3)
        
    @unittest.skipUnless(MF.version==1, "跳过测试")     #不满足条件则跳过
    def test_003(self):
        result = MF.add(0, 3)
        self.assertEqual(result, 3)
if __name__=="__main__":
    unittest.main()

4.expectedFailure

import unittest
from test import myfunction as MF

class MyTest(unittest.TestCase):
    def setUp(self):
        pass
    
    def tearDown(self):
        pass

    def test_001(self):
        result = MF.add(1, 2)
        self.assertEqual(result, 3,"预期是%d,实际是%d")
    
    @unittest.expectedFailure
    def test_002(self):
        result = MF.add(0, 2)
        self.assertEqual(result, 3)
    
if __name__=="__main__":
    unittest.main()

扫描所有测试用例

unittest.defaultTestLoader.discover

    def set_test_suite(self):
        """
        set case suite
        :return:
        """
        #定义测试套件
        #定义测试文件查找的目录
        test_suite = unittest.TestSuite()
        testcase_dir = os.path.join(RC.proDir,"testcase")
        discover = unittest.defaultTestLoader.discover(testcase_dir, pattern = 'TS*.py', top_level_dir=None)
        #扫描所有TS开头的py文件,然后把测试用例添加到测试集合
        if discover:
            for suite in discover:
                for test_case in suite:
                    test_suite.addTest(test_case)
        else:
            return None
        return test_suite

生成测试报告HTMLTestRunner

def runAllTest(self):
        """
        run test
        :return:
        """
        try:
            suite = self.set_test_suite()
            if suite:
                logger.info("********TEST START********")
                fp = open(reportPath, 'wb')
                runner = HT.HTMLTestRunner(stream=fp, 
                                           verbosity=2,
                                           title='Test Report', 
                                           description='Test Description')
                runner.run(suite)
                fp.close()
                time.sleep(3)
            else:
                logger.info("Have no case to test.")
        except Exception as ex:

补充

1.unittest.main()执行过程
收集测试用例
根据case生成测试集
运行测试用例

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
 跨浏览器,可兼容IE7--IE10, FireFox, Chrome, Opera等几大内核的浏览器,且不需要浏览器再加装任何控件。  多系统兼容性、可移植性:由于只包括前台UI,因此二次开发者可很方便将本插件用在任何一种需要流程图的B/S系统应用上,流程图的详细实现逻辑完全交于后台程序开发者自己实现;对于后台,只要能返回/接收能被本插件解析的JSON格式数据即可.所以本插件可用于不同的服务器语言建立的后台上.  跨领域:流程图设计器不止用在电信领域,在其它需要IT进行技术支持的领域中都有重大作用.  以下从纯技术实现层面具体描述:  页面顶部栏、左边侧边栏均可自定义;  当左边的侧边栏设为不显示时,为只读状态,此时的视图区可当作是一个查看器而非编辑器。  侧边工具栏除了基本和一些流程节点按钮外,还自定义新的节点按钮,自定义节点都可以有自有的图标、类型名称,定义后在使用可可在工作区内增加这些自定义节点。  顶部栏可显示流程图数据组的标题,也可提供一些常用操作按钮。  顶部栏的按钮,除了撤销、重做按钮外,其余按钮均可自定义点击事件。  可画直线、折线;折线还可以左右/上下移动其中段。  具有区域划分功能,能让用户更直观地了解哪些节点及其相互间的转换,是属于何种自定义区域内的。  具有标注功能,用橙红色标注某个结点或者转换线,一般用在展示流程进度时。  能直接双击结点、连线、分组区域中的文字进行编辑  在对结点、连线、分组区域的各种编辑操作,如新增/删除/修改名称/重设样式或大小/移动/标注时,均可捕捉到事件,并触发自定义事件,如果自定义事件执行的方法返回FALSE,则会阻止操作。  具有操作事务序列控制功能,在工作区内的各种有效操作都能记录到一个栈中,然后可以进行撤销(undo())或重做(redo()),像典型的C/S软件一样。  0.4版中,加入了只导出在初始载入后被编辑的流程图中,只作了增删改等变更的元素,这样可用于用户快速存储,只保存本次变更过的内容,不用重新保存整个流程。  0.5版中,结点的样式不再受到原有程序的限制,所有样式均默认为淡蓝色长方形;如果要指定为圆形,可在初始化时定义结点类型为”原有类型”+” round”;如果要指定为复合结点,则可在初始化时定义结点类型为”原有类型”+” mix”。”原有类型”+” myType”:myType可为自己写的一种特殊样式类.  0.6版中,修正了一些BUG,改善了用户操作体验,并增加在可编辑状态下时,能用键盘上DELETE按键对元素进行删除功能。  0.7版中,修正了一些BUG,增加了连线变更要连的起始结点或结束结点的功能。  0.8版,取消原来的拟物化页面,变成如今的扁平化页面,并且支持主要位置的颜色自定义功能(如果想沿用原来老版本中的拟物化页面,只需保留原来的GooFlow.css文件即可);修正0.7版中的画线BUG。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值