一、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的参数用法同