单元测试框架之Unittest

一、Unittest简介

1. 工作原理

  • TestCase:在unittest中的一个TestCase的实例就是一个测试用例,包括测试前资源初始化(setUp),执行测试代码(testXXX),测试后环境的还原tearDown)。
  • TestSuite:测试套件:多个独立的测试用例(test case)可以构成一个测试套件,然后传递给TestRunner进行测试执行。
  • TestLoader:通过unittest.TestLoader类的loadTestsFromTestCase、loadTestsFromModule、LoadTestsFromName、discover方法,可以将测试用例添加一个测试套件中。
  • TestRunner :测试集的运行器,可以在其基础上扩展子类TextTestRunner或者HTMLTestRunner,只不过生成的测试报告样式不同。
  • TestResult:测试结果类,用来处理测试用例或测试集执行过程中的所有信息并最终输出。比如代码错误、异常、断言失败、skip等

2. 作用

  • 单元测试
  • 组织测试用例与执行
  • 有丰富的断言方法
  • 有丰富的报告和日志模板

二、Unittest使用

以下是Unittest被测代码,使用Unittest进行单元测试

class mathTest1:

    def mathJia(self,a,b):
        return a + b

    def mathJian(self,a,b):
        return a - b

    def mathCheng(self,a,b):
        return a * b

    def mathChu(self,a,b):
        if b == 0:
            return "非法输入"
        else:
            return a / b

1. TestCase

  • setUp():测试前资源初始化,测试用例的前提条件可以写在这里
  • testXXX():测试用例方法,在这儿编写测试代码,默认根据位数的数字来运行。如:testA1()比testA2()先执行
  • tearDown():测试用例的资源释放
import unittest
from unittest import TestCase
from mathTest.mathClass import mathTest1


class unitMathTest2(TestCase):
	# 如果不需要,可以不写
    def setUp(self):
        self.mt = mathTest1()
        print("setUp")

	# 如果不需要,可以不写
    def tearDown(self):
        self.mt = None
        print("tearDown")

	# 方法名位数为1,先执行
    def testJian1(self):
        actualRes = self.mt.mathJian(1,1)
        expectRes = 0
        self.assertEqual(actualRes,expectRes,"预期结果与实际结果不一致")


    def testJian2(self):
        actualRes = self.mt.mathJian(-1, 1)
        expectRes = -2
        self.assertEqual(actualRes, expectRes, "预期结果与实际结果不一致")


    def testJian3(self):
        actualRes = self.mt.mathJian(6, 1)
        expectRes = 5
        self.assertEqual(actualRes, expectRes, "预期结果与实际结果不一致")

    @unittest.expectedFailure
    def testJian4(self):
        actualRes = self.mt.mathJian(-1,)
        expectRes = 0
        self.assertEqual(actualRes, expectRes, "预期结果与实际结果不一致")

1.1 unittest注解

TestCase使用注解,可以为用例指定特定的含义

  • @classmethod:标记该方法为类初始化方法setUpClass()、tearDownClass(),会比setup和tearDown更先执行
  • @unittest.skipif(conditition,reason):condititon为条件,当条件为true时则跳过测试
  • @unittest.skip(reason):无条件跳过测试,reason描述为什么跳过测试
  • @unittest.skipunless(condition,reason):condition为条件,与上面相反,当条件不是true时则跳过测试
  • @unittest.expectedFailure:标记该测试预期为失败 ,如果该测试方法运行失败,则该测试不算做失败

如果在方法上添加了跳过和预期失败的注释将不会出现在报告里。

2. TestSuit

通过unittest.TestSuite()类直接构建,可以通过TestSuite实例的addTest、addTests方法为测试套件添加用例

from unittest import TestCase
import unittest
from mathDemo import mathDemo


class mathtest(TestCase):
    def setUp(self):
        self.md = mathDemo()
        print("setUp")

    def tearDown(self):
        self.md = None
        print("tearDown")

    def testAdd01(self):
        print("testAdd01")
        actualValue = self.md.jia(12, 23)
        expectValue = 35
        # self.assertEqual(actualValue,expectValue,"预期结果和实际结果不一致")

    def testAdd02(self):
        print("testAdd02")
        actualValue = self.md.jia(0, 23)
        expectValue = 23
        # self.assertEqual(actualValue, expectValue, "预期结果和实际结果不一致")


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

	# 方式一:一个一个地添加
    suite = unittest.TestSuite()
    suite.addTest(mathtest("testAdd02"))
    suite.addTest(mathtest("testAdd01"))
    print(suite)

	# 方式二:封装成map集合一起添加
    # suite.addTests(map(mathtest, ["testAdd02", "testAdd01"]))

	# 方式三:通过名字添加
    # loader = unittest.TestLoader
    # suite.addTest(loader.loadTestsFromTestCase(mathtest))
    # loadTestsFromName可以是用例名、类名、模块名
    # suite.addTest(loader.loadTestsFromName("mathtest"))
    # print(suite)


    # 方式四:重要 discover(path,pattern)通过模块名模糊添加
    # 从指定的path路径下,查找符合要求py文件,比如查找patern='math*.py'
    # suite = unittest.defaultTestLoader.discover(r"./unittestDemo",pattern="math*.py")
    # print(suite)

    result = unittest.TestResult()
    # 测试执行
    suite.run(result)
    # print(result)
    # print(result.__doc__)

3. TestLoader

  • 通过unittest.TestLoader类的loadTestsFromTestCase、loadTestsFromModule、loadTestsFromName、discover的四种方式,可以将测试用例添加一个测试套件中
  • 详见上面代码

4. TestRunner

TextTestRunner 语法:TextTestRunner(stream=XXX, descriptions=XXX, verbosity=0)

  • Stream:运行测试用例之后的结果要保存的文本文件流对象
  • Descriptions:对生成报告的描述性文字
  • Verbosity:日志显示等级
    verbosity =< 0时—静默模式,输出结果中不提示执行成功的用例数
    verbosity = 1 时—默认模式,输出结果中仅仅以(.)的形式表示执行成功的用例数,成功几个就是几个点
    verbosity >= 2 时—详细模式,可以输出每个用例执行的详细信息,特别是在用例较多的情况下,此设置会比较有用
discover = unittest.defaultTestLoader.discover(r"XXX/XXX",pattern="XXX*.py")
with open(r"./XXX/res.log","a") as f:
	runner = unittest.TextTestRunner(stream=f,descriptions="XXX测试报告",verbosity=2)
	runner.run(discover)

5. TestTesult

测试集合执行完毕之后,会返回TestResult对象,可以通过__dict__获取其中的字典格式的执行结果数据。

suit1 = unittest.TestSuit()
suit1.addTest(loader.loadTestsFromTestCase(mathtest))
run = unittest.TestResult()
suit1.run(r.__dic__)
  • failfast:值为True或False,当设置为True时,测试过程中遇到失败或者错误,则立即终止后续的测试
  • failures:失败,这里会列出使用断言方法失败的情况。
  • errors:错误,这里会列出程序出现的异常错误。
  • testsRun:已经运行的所有测试的数量。
  • skipped:列出跳过的测试方法及原因。
  • expectedFailures:列出预期失败的方法。
  • unexpectedSuccesses:列出标记为预期失败,但实际运行却又成功的方法。

三、Unittest断言

一个自动化测试用例,测试步骤、测试的断言缺一不可,unittest中提供的断言方法有:

  • assertEqual(a,b,msg=""):就是判断a和b是否相等,如果相等,则断言成功,如果不相等,会断言失败,并且输出msg消息
  • assertNotEqual(a,b,msg=""):就是判断a和b是否不相等
  • assertTrue(a):就是判断a是否为True这个bool值
  • assertFalse(a):就是判断a是否为False这个bool值
  • assertIs(a,b,msg=""):判断a和b的内存地址是否相等,如果相等则身份一致
  • assertIsNot(a,b,msg):判断a和b的内存地址是否不相等,如果相等,断言失败
  • assertIsNone(a):判断对象a是不是空指针(没有指向堆内存中空间),如果是则断言成功
  • assertIsNotNone(a):判断对象a是不是空指针,如果是,则断言失败
  • assertIn(a,b):判断a是不是b的成员,如果是则断言成功
  • assertNotIn((a,b):判断a是不是b的成员,如果不是则断言成功
  • assertIsInstance(a,b):判断a是b的一个实例对象
  • assertIsNotInstance(a,b):判断a不是b的一个实例对象

四、主执行文件

在测试用例、测试文件比较多的时候,使用统一的主测试执行文件进行测试用例的执行非常方便,这就需要结合discover方法和TextTestRunner进行。

import unittest
from HTMLTestRunner import HTMLTestRunner
import time
import os

discover = unittest.defaultTestLoader.discover(r"./20200408/",pattern="myMathTest*.py")

#使用runner运行器运行测试集
with open(r"./20200408/res.txt","w",encoding="utf-8") as f:
     runner = unittest.TextTestRunner(f,descriptions="用于测试math类的用例执行",verbosity=2)
     runner.run(discover)

五、HTMLTestRunner执行生成报告

import unittest
from HTMLTestRunner import HTMLTestRunner
from logModule.mailsend1 import mailsend


discover = unittest.defaultTestLoader.discover(r"XXX\mathTest",pattern="math_*.py")

with open(r"XXX\mathTest\re.html","wb") as f:
    runner = HTMLTestRunner(stream=f,verbosity=-1,title="单元测试用例报告",description="单元测试用例报告")
    runner.run(discover)

# 所有用例执行完成后可以调用mailsend进行邮件发送
ssend = mailsend()
ssend.sendFujian(r"result")
  • HTMLTestRunner与上面TextTestRunner的参数用法同

猜你喜欢

转载自blog.csdn.net/yang_yang_heng/article/details/108740493
今日推荐