test case:测试用例
unittest 提供一个基类 TestCase
,用于新建测试用例。一个测试用例是一个独立的测试单元,unittest中测试用例方法都是以test开头的,且执行顺序会按照方法名的ASCII值排序。
test suite:测试套件
多个测试用例的集合,把需要一起执行的测试用例集中放到一块执行,使用TestLoader来加载测试用例到测试套件中。
test fixure:测试夹具
用于测试用例环境的搭建和销毁。测试前环境准备(setUp),执行测试代码(run),以及测试后环境还原(tearDown)。
test loader 测试加载器
加载TestCase到TestSuite中的,并创建它们的实例,然后添加到TestSuite中,返回TestSuite实例。
test runner 测试运行器
执行测试用例,并将测试结果保存到TextTestResult实例中。默认选择TextTestRunner(文本运行器),这个运行器可能使用图形接口、文本接口,或返回一个特定的值表示运行测试的结果。
注:这里编译器使用的pyCharm 进行演示。
import unittest # 导入unittext包
class StadyUnittest(unittest.TestCase): # 定义类,继承unittest.TestCase方法
# 测试用例 1
def test01_case1(self):
print("测试1:case1")
# 测试用例 2
def test02_case2(self):
print("测试2:case2")
# 测试用例 3
def test03_case3(self):
print("测试3:case3")
注意这里无需写 main 函数也可以执行。鼠标在哪一个函数后面右击运行,即可对哪一个函数进行单元测试。如下图,选择测试 test01_case() 函数:
测试 test03_case3() 函数:
如果想执行所有测试用例,在类名处点击执行即可。
python -m unittest stady_test.StadyUnittest
命令可以达到上述相同的执行效果。-v
参数可以输出更详细的报告。另外,我们也可以只指定类中的某个测试用例执行。-k
匹配测试用例选择那些测试用例去执行。其中 -k 有两种匹配方法。
选项 | 说明 |
---|---|
-b, --buffer | 在测试运行时,标准输出流与标准错误流会被放入缓冲区。成功的测试的运行时输出会被丢弃;测试不通过时,测试运行中的输出会正常显示,错误会被加入到测试失败信息。 |
-c, --catch | 当测试正在运行时, Control-C 会等待当前测试完成,并在完成后报告已执行的测试的结果。当再次按下 Control-C 时,引发平常的 KeyboardInterrupt 异常。 |
-f, --failfast | 当出现第一个错误或者失败时,停止运行测试。 |
-k | 只运行匹配模式或子串的测试方法和类。可以多次使用这个选项,以便包含匹配子串的所有测试用例。 包含通配符(*)的模式使用 fnmatch.fnmatchcase() 对测试名称进行匹配。另外,该匹配是大小写敏感的。 模式对测试加载器导入的测试方法全名进行匹配。 例如,-k foo 可以匹配到 foo_tests.SomeTest.test_something 和 bar_tests.SomeTest.test_foo ,但是不能匹配到 bar_tests.FooTest.test_something 。 |
–locals | 在回溯中显示局部变量。 |
-v, --verbose | 更详细地输出结果。 |
-s, --start-directory directory | 开始进行搜索的目录(默认值为当前目录 . )。 |
-p, --pattern pattern | 用于匹配测试文件的模式(默认为 test*.py )。 |
-t, --top-level-directory directory | 指定项目的最上层目录(通常为开始时所在目录)。 |
stady_test.StadyUnittest.test01_case1
已经在配置中为我们生成了。在源文件中添加如下代码:
if __name__ == '__main__':
unittest.main()
如果想以这种方式运行程序,我们需要配置一下编译器。
python stady_test.py
。测试结束在屏幕输出的文本信息中有四种类型:
形式 | 表示类型 |
---|---|
. |
成功 |
F |
失败 |
E |
错误 |
S |
跳过 |
下面使用如下代码,分别输出上述四种状态。
import unittest # 导入unittext包
class StadyUnittest(unittest.TestCase): # 定义类,继承unittest.TestCase方法
# 测试用例 1:正常执行
def test01_case1(self):
print("测试1:case1")
# 测试用例 2:异常
def test02_case2(self):
raise Exception("测试用例2 异常")
print("测试2:case2")
# 测试用例 3:错误
def test03_case3(self):
print("测试3:case3")
self.assertEqual(1, 2) # 断言出错
# 测试用例 4:跳过
@unittest.skip("跳过case4") # 装饰器,用于跳过此用例
def test04_case4(self):
print("测试4:case4")
if __name__ == '__main__':
unittest.main()
unittest.main()
方法默认执行所有的测试用例,并且测试用例的执行是按照一定的顺序执行的 。
上述代码中,我们设置了四个测试单元函数,他们的函数名均以test开头,编号为 00、01、02、03 。目的就是让他们按照一定的执行顺序执行下去。
因为,单元测试用例的执行顺序就是以ascll码的顺序执行的。例如我们可以新建一个python文件,写下如下代码进行测试。
import unittest # 导入unittext包
class StudyUnittest(unittest.TestCase): # 定义类,继承unittest.TestCase方法
def testa_case(self):
print("case4 : testa_...")
def testZ_case(self):
print("case3 : testZ_...")
def test01_case(self):
print("case1 : test01_...")
def test20_case(self):
print("case2 : test20_...")
if __name__ == '__main__':
name = [a for a in dir(StudyUnittest) if a.startswith('test')]
print("name: ", name)
print("sort: ", sorted(name)) # 输出排序后的 name
print("执行顺序:")
unittest.main()
执行结果:按照 0 ~ 9,A ~ Z,a ~ z 的顺序。
unittest.main() 函数有很多参数,下面是unittest文件中 main() 的函数声明
unittest.main(
module='__main__', # 测试用例所在的路径,__main__表示当前模块
defaultTest=None, # 待测用例额的名称,默认是所有
argv=None, # 接收外部的参数
testRunner=None, # 测试运行器,默认是文本测试运行器 TextTestRunner
testLoader=unittest.defaultTestLoader, # 测试用例加载器
exit=True, # 是否在测试完成之后退出程序
verbosity=1, # 表示测试结果的信息复杂度,有0、1、2 三个值. -v
failfast=None, # 用例遇到失败后停止,不在执行后面的用例 -f
catchbreak=None, # ctrl-c 中断后执行完输出测试结果 -c
buffer=None, # 在测试运行期间缓冲stdout和stderr -b
warnings=None # 显示警告信息
)
以上为 unittest.main() 中的参数信息,下面简单的测试一下其中的几个参数选项:
if __name__ == '__main__':
unittest.main(defaultTest=["StudyUnittest.test02_case"])
-v
选项,0,1,2分别表示输出结果的详细程度,默认为1 。if __name__ == '__main__':
print("\n_______________v = 0 :")
unittest.main(verbosity=0,exit=False)
print("\n_______________v = 1 :")
unittest.main(verbosity=1,exit=False)
print("\n_______________v = 2 :")
unittest.main(verbosity=2,exit=True)
if __name__ == '__main__':
a = unittest.main(argv=['study_test3.py', 'StudyUnittest.test01_case'])
使用TestSuite进行测试的步骤有三步:
unittest.TestSuite()
生成一个套件实例addTest()
或 addTests()
把测试用例加载到套件中。import unittest
class StudyUnittest(unittest.TestCase):
def test01_case(self):
print("test01_case")
def test02_case(self):
print("test02_case")
def test03_case(self):
print("test03_case")
if __name__ == '__main__':
suite = unittest.TestSuite() # 生成一个套件
suite.addTest(StudyUnittest("test01_case")) # 加载测试用例到套件
suite.addTest(StudyUnittest("test02_case")) # 加载测试用例到套件
unittest.main(defaultTest='suite') # 选择默认执行的测试用例为 suilt 套件
除此之外,我们也可以使用测试运行器运行该套件。
if __name__ == '__main__':
suite= unittest.TestSuite() # 生成一个套件
suite.addTest(StudyUnittest("test01_case")) # 加载测试用例到套件
suite.addTest(StudyUnittest("test02_case")) # 加载测试用例到套件
# unittest.main(defaultTest='suilt') # 选择默认执行的测试用例为 suilt 套件
# 使用运行器运行该套件
unittest.TextTestRunner().run(suite)
因为调用 unittest.main() 方法,最终都将转为调用 unittest.TextTestRunner() 方法。
而在 TextTextRunner 中实际上调用的 run() 方法执行的。
由此,我们可以使用函数定义我们的测试套件,通过以下方式进行测试:
# 定义测试套件生成函数
def suite():
suite = unittest.TestSuite()
suite.addTest(StudyUnittest("test01_case")) # 加载测试用例到套件
suite.addTest(StudyUnittest("test02_case")) # 加载测试用例到套件
return suite
if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(suite())
在pytthon 开发文档中显示 unittest.TestSuite()
中也存在一个 run()
方法。
unittest.TestSuite().run()
运行与此套件关联的测试,将结果收集到作为结果传递的测试结果对象中。 请注意,与TestCase.run()不同,TestSuite.run()要求传递结果对象。同时我们可以查看TestCase类中,也有一个 run() 方法。
而我们之前提到的,unittest.main()的执行最终是调用了TextTestRunner.run()方法,而TextTestRunner.run()方法又根据每个suite去调用TestSuite.run()或TestCase.run() 去执行测试的。
if __name__ == '__main__':
#实例化一个TextTestResult类
result = unittest.TextTestResult(sys.stdout,'test result',1)
suite = unittest.TestSuite(map(StudyUnittest,['test01_case']))
'''
上面语句等同于 创建一个TestSuite实例,并向其中添加一个estCase
suite = unittest.TestSuite()
suite.addTest(StudyUnittest('test01_case'))
'''
suite.run(result)
# 使用TestCase实例的run()方法执行
testcase = StudyUnittest('test02_case')
testcase.run(result)
addTests() 接收的参数是一个列表类型,函数原型:def addTests(self, tests: Iterable[_TestType]) -> None:
。
下面演示用法:
if __name__ == '__main__':
suite = unittest.TestSuite()
testcases = [ # 测试用例集
StudyUnittest("test01_case"),
StudyUnittest("test02_case")
]
suite.addTests( testcases) # 加载测试用例集
unittest.main(defaultTest='suite')
我们发现使用 addTests() 与使用 addTest() 加载测试用例都太过麻烦,那么我们可以使用测试加载器进行测试用例的加载。
我们使用TestLoader 类中提供的discover()方法。 discover(start_dir,pattern='test*.py',top_level_dir= None)
import os
import unittest
class StudyUnittest(unittest.TestCase):
def test01_case(self):
print("test01_case")
def test02_case(self):
print("test02_case")
def test03_case(self):
print("test03_case")
if __name__ == '__main__':
suite = unittest.TestSuite()
testcases = unittest.defaultTestLoader.discover(
start_dir=os.getcwd(), # 目录:os.getcwd()当前目录路径
pattern='*.py' # 文件: 所有以 .py 结尾的文件
)
suite.addTests( testcases) # 加载测试用例集
unittest.main(defaultTest='suite')
除了上述示例中的discover()方法,TestLoader还提供了其他方法,下面将列出一部分内容仅供参考。
TestLoader类用于从类和模块创建测试套件。通常,不需要创建该类的实例;unittest模块提供了一个可以作为unittest. defaulttestloader共享的实例。但是,使用子类或实例允许自定义一些可配置属性。
返回由TestCase派生的testCaseClass中包含的所有测试用例的套件。
将为每个由getTestCaseNames()命名的方法创建一个测试用例实例。 默认情况下,这些是以test开头的方法名称。 如果getTestCaseNames()不返回任何方法,但实现了runTest()方法,则将为该方法创建一个测试用例。
给定一个字符串说明符,返回所有测试用例的套件。
指定者名称是“点名”,可以解析为模块,测试用例类,测试用例类中的测试方法,TestSuite实例或返回TestCase或TestSuite实例的可调用对象。 这些检查按照此处列出的顺序进行; 也就是说,将在可能的测试用例类上的方法选择为“测试用例类内的测试方法”,而不是“可调用对象”。