unittest单元测试框架不仅可以适用于单元测试,还可以适用接口测试、Web自动化测试等,它有一个很重要的特性:它是通过类(class)的方式,将测试用例组织在一起。
unittest框架核心要素
Unittest框架中最核心的四个概念是:test case, test suite, test runner, test fixture。
unittest框架常用模块
- unittest.TestCase:TestCase类,所有测试用例类继承的基本类
- unittest.TestSuite():unittest框架的TestSuite()类是用来创建测试套件的。
- unittest.main(): 将一个单元测试模块变为可直接运行的测试脚本,main()方法使用TestLoader类来搜索所有包含在该模块中以“test”命名开头
的测试方法,并自动执行他们,根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9,A-Z,a-z。 - unittest.TextTestRunner(): unittest框架的TextTestRunner()类,通过该类下面的run()方法来运行suite所组装的测试用例,入参为suite测试套件。
unittest用法
- 导入unittest模块、被测文件或者其中的类
- 创建一个测试类,并继承unittest.TestCase
- 重写setUp和tearDown方法(如果有初始化条件和结束条件)
- 定义测试函数,函数以test_开头
- 在函数体中使用断言来判断测试结果是否符合预期结果
- 调用unittest.main()方法来运行测试用例
# 导入unittest模块
import unittest
from calculator import Calculator
# 定义测试类,父类为unittest.TestCase
class TestCalculator(unittest.TestCase):
# 定义setUp()方法用于测试用例执行前的初始化工作
# 所有类中方法的入参为self,定义方法的变量也要“self.变量”
def setUp(self):
print('test start')
self.result = Calculator(10, 5)
# 定义测试用例,以“test_”开头命名的方法,方法的入参为self
# 可使用unittest.TestCase类下面的各种断言方法用于对测试结果的判断
# 可定义多个测试用例
def test_001_minus(self):
"""Test method minus(a, b)"""
print('测试减法函数')
self.assertEqual(self.result.minus(), 5, '计算错误!')
# 定义tearDown()方法用于测试用例执行之后的善后工作。
# 注意,方法的入参为self
def tearDown(self):
print('test end')
if __name__ == '__main__':
# unittest.main()方法会搜索该模块下所有以test开头的测试用例方法,并自动执行它们。
unittest.main(verbosity=2)
verbosity是一个选项,表示测试结果的信息复杂度,有0、1、2 三个值
0 (静默模式): 你只能获得总的测试用例数和总的结果 比如 总共10个 失败2 成功8
1 (默认模式): 非常类似静默模式 只是在每个成功的用例前面有个“.” 每个失败的用例前面有个 “F”
2 (详细模式):测试结果会显示每个测试用例的所有相关的信息
并且 你在命令行里加入不同的参数可以起到一样的效果
加入 --quiet 参数 等效于 verbosity=0
加入–verbose参数等效于 verbosity=2
什么都不加就是 verbosity=1
组织TestSuite
unittest执行测试用例,默认是根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9,A-Z,a-z。
import unittest
class MyTest(unittest.TestCase):
def test_1(self):
print('test_1')
def test_2(self):
print('test_2')
if __name__ == '__main__':
suite = unittest.TestSuite()
# 测试用例的执行顺序,按添加的顺序执行
suite.addTest(MyTest('test_2'))
suite.addTest(MyTest('test_1'))
# 3. 运行容器中的测试用例
runner = unittest.TextTestRunner()
runner.run(suite)
TestLoader.loadTestsFromTestCase加载多个类
import unittest
from test_myclass1 import TestCalculator1
from test_myclass2 import TestCalculator2
if __name__ == '__main__':
# 根据给定的测试类,获取其中所有以“test”开头的测试方法,并返回一个测试套件
suite1 = unittest.TestLoader().loadTestsFromTestCase(TestCalculator1)
suite2 = unittest.TestLoader().loadTestsFromTestCase(TestCalculator2)
suite3 = unittest.TestLoader().loadTestsFromTestCase(TestCalculator2)
# 将多个测试类加载到测试套件中
# 通过调整suit2和suite1的顺序,可以设定执行顺序
suite = unittest.TestSuite([suite1, suite2,suite3])
# 设置verbosity = 2,可以打印出更详细的执行信息
unittest.TextTestRunner(verbosity=2).run(suite)
TestLoader.discover方法匹配目录下的用例
假如现在目录下存在两个测试用例,Test_Myclass.py和Test_Myclass2.py,如果用addTests的方法添加
用例到测试套件,未免有点麻烦,这时候需要使用TestLoader()这个类。
import os
import unittest
if __name__ == '__main__':
# 实例化测试套件对象
suite = unittest.TestSuite()
# 1.实例化TestLoader对象
# 2.使用discover去找到一个目录下的所有测试用例
loader = unittest.TestLoader()
# 3.使用addTests将找到的测试用例放在测试套件下
# os.getcwd()返回current working directory
suite.addTests(loader.discover(os.getcwd()))
# 或者另外一种写法
test_dir = './test'
suite = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')
# 运行
runner = unittest.TextTestRunner()
runner.run(suite)
将运行结果输出到文件
# 执行测试集合
with open('UnittestTextReport.txt', 'a') as f:
runner = unittest.TextTestRunner(stream=f, verbosity=2)
runner.run(suite)
unittest常见用法
setUp和setDown
- setUp() 和 tearDown() 两个方法(其实是重写了TestCase的这两个方法),这两个方法在每个测试方法执行前以及执行后执行一次,setUp用来为测试准备环境,tearDown用来清理环境。
- 如果想要在所有case执行之前准备一次环境,并在所有case执行结束之后再清理环境,我们可以用setUpClass() 与 tearDownClass()。
def setUp(self):
print('test start')
self.result = Calculator(10, 5)
def tearDown(self):
print('test end')
@classmethod
def setUpClass(cls):
print('仅测试套件前执行一次')
@classmethod
def tearDownClass(cls):
print('仅测试套件结束后执行一次')
跳过某个Case
如果我们临时想要跳过某个case不执行怎么办?unittest也提供了两种方法:
1 skip装饰器
- @unittest.skip(reason): skip(reason)装饰器:无条件跳过装饰的测试,并说明跳过测试的原因。
- @unittest.skipIf(reason): skipIf(condition,reason)装饰器:条件为真时,跳过装饰的测试,并说明跳过测试的原因。
- @unittest.skipUnless(reason): skipUnless(condition,reason)装饰器:条件为假时,跳过装饰的测试,并说明跳过测试的
原因。 - @unittest.expectedFailure(): expectedFailure()测试标记为失败。
@unittest.skip("I don't want to run this case.")
def test_add(self):
"""Test method add(a, b)"""
print('测试加法函数')
self.assertEqual(self.result.add(), 15, '计算错误!')
@unittest.skipIf(3 > 2, "3大于2,此用例不执行")
def test_minus(self):
"""Test method minus(a, b)"""
print('测试减法函数')
self.assertEqual(self.result.minus(), 5, '计算错误!')
# 除非是linux平台,否则忽略此方法,win32是windows平台
@unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
def test_skip(self):
"""Test method skip(a, b)"""
print('测试跳过函数')
self.assertEqual(self.result.divide(), 2, '计算错误!')
2、TestCase.skipTest()方法
def test_multip(self):
"""Test method multi(a, b)"""
# 跳过
self.skipTest('Do not run this.')
print('测试乘法函数')
self.assertEqual(self.result.multip(), 50, '计算错误!')
Unittest常见用法:断言方法
- assertEqual(a,b,[msg=‘失败时打印的信息’]):断言a,b是否相等,相等则用例通过。
- assertNotEqual(a,b,[msg=‘失败时打印的信息’]):断言a,b是否相等,不相等则试用例通过。
- assertTrue(x,[msg=‘失败时打印的信息’]):断言x是否True,是True则用例通过。
- assertFalse(x,[msg=‘失败时打印的信息’]):断言x是否False,是False则用例通过。
- assertIs(a,b,[msg=‘失败时打印的信息’]):断言a是否是b,是则用例通过。
- assertNotIs(a,b,[msg=‘失败时打印的信息’]):断言a是否是b,不是则用例通过。
- assertIsNone(x,[msg=‘失败时打印的信息’]):断言x是否None,是None则用例通过。
- assertIsNotNone(x,[msg=‘失败时打印的信息’]):断言x是否None,不是None则用例通过。
- assertIn(a,b,[msg=‘失败时打印的信息’]):断言a是否在b中,在b中则用例通过。
- assertNotIn(a,b,[msg=‘失败时打印的信息’]):断言a是否在b中,不在b中则用例通过。
- assertIsInstance(a,b,[msg=‘失败时打印的信息’]):断言a是b的一个实例,是则用例通过。
- assertNotIsInstance(a,b,[msg=‘失败打印的信息’]):断言a不是b的一个实例,不是则用例过。
import unittest, random
# 被测试类
class MyClass(object):
@classmethod
def sum(cls, a, b):
return a + b
@classmethod
def div(cls, a, b):
return a / b
@classmethod
def retrun_None(cls):
return None
# 单元测试类
class MyTest(unittest.TestCase):
# assertEqual()方法实例
def test_assertEqual(self):
# 断言两数之和的结果
try:
a, b = 1, 2
sum = 3
self.assertEqual(a + b, sum, '断言失败,%s + %s != %s' % (a, b, sum))
except AssertionError as e:
print(e)
# assertNotEqual()方法实例
def test_assertNotEqual(self):
# 断言两数之差的结果
try:
a, b = 5, 2
res = 1
self.assertNotEqual(a - b, res, '断言失败,%s - %s != %s' % (a, b, res))
except AssertionError as e:
print(e)
# assertTrue()方法实例
def test_assertTrue(self):
# 断言表达式的为真
try:
self.assertTrue(1 == 1, "表达式为假")
except AssertionError as e:
print(e)
# assertFalse()方法实例
def test_assertFalse(self):
# 断言表达式为假
try:
self.assertFalse(3 == 2, "表达式为真")
except AssertionError as e:
print(e)
# assertIs()方法实例
def test_assertIs(self):
# 断言两变量类型属于同一对象
try:
a = 12
b = a
self.assertIs(a, b, "%s与%s不属于同一对象" % (a, b))
except AssertionError as e:
print(e)
# test_assertIsNot()方法实例
def test_assertIsNot(self):
# 断言两变量类型不属于同一对象
try:
a = 12
b = "test"
self.assertIsNot(a, b, "%s与%s属于同一对象" % (a, b))
except AssertionError as e:
print(e)
# assertIsNone()方法实例
def test_assertIsNone(self):
# 断言表达式结果为None
try:
result = MyClass.retrun_None()
self.assertIsNone(result, "not is None")
except AssertionError as e:
print(e)
# assertIsNotNone()方法实例
def test_assertIsNotNone(self):
# 断言表达式结果不为None
try:
result = MyClass.sum(2, 5)
self.assertIsNotNone(result, "is None")
except AssertionError as e:
print(e)
# assertIn()方法实例
def test_assertIn(self):
# 断言对象A是否包含在对象B中
try:
strA = "this is a test"
strB = "is"
self.assertIn(strB, strA, "%s不包含在%s中" % (strB, strA))
except AssertionError as e:
print(e)
# assertNotIn()方法实例
def test_assertNotIn(self):
# 断言对象A不包含在对象B中
try:
strA = "this is a test"
strB = "Selenium"
self.assertNotIn(strB, strA, "%s包含在%s中" % (strB, strA))
except AssertionError as e:
print(e)
# assertIsInstance()方法实例
def test_assertIsInstance(self):
# 测试对象A的类型是否值指定的类型
try:
x = MyClass
y = object
self.assertIsInstance(x, y, "%s的类型不是%s" % (x, y))
except AssertionError as e:
print(e)
# assertNotIsInstance()方法实例
def test_assertNotIsInstance(self):
# 测试对象A的类型不是指定的类型
try:
a = 123
b = str
self.assertNotIsInstance(a, b, "%s的类型是%s" % (a, b))
except AssertionError as e:
print(e)
if __name__ == '__main__':
# 执行单元测试
unittest.main()
Unittest常见用法:命令行执行用例方法
unittest 支持用例自动(递归)发现:
1、默认发现当前目录下所有符合 test*.py 测试用例
使用 python -m unittest 或 python -m unittest discover
2、通过 -s 参数指定要自动发现的目录, -p 参数指定用例文件的名称模式
python -m unittest discover -s project_directory -p “test_*.py”
3、通过位置参数指定自动发现的目录和用例文件的名称模式
python -m unittest discover project_directory “test_*.py”
unittest 支持执行指定用例:
1、指定测试模块
python -m unittest test_module1 test_module2
2、指定测试类
python -m unittest test_module.TestClass
3、指定测试方法
python -m unittest test_module.TestClass.test_method
4、指定测试文件路径(仅 Python 3)
python -m unittest tests/test_something.py