Unittest单元测试框架
1、unittest是什么
unittest是python内置的单元测试框架,具备编写用例、组织用例、执行用例、输出报告等自动化框架的条件
2、单元测试框架的优点
一般来说不用单元测试框架也能编写单元测试,因为单元测试本身就是通过一段代码去验证另一段代码,所以不用单元测试框架也能编写单元测试。只是使用框架时会有更多的优点
1、提供用例组织与执行:
当测试用例达到成百上千条时,就产生了扩展性与维护性等问题,此时就需要考虑用例的规范与组织问题了。单元测试框架便能很好的解决这个问题
2、提供丰富的比较方法:
不论是功能测试还是单元测试,在用例完成之后都需要将实际结果与预期结果进行比较(断言),从而断定用例是否执行通过。单元测试框架一般会提供丰富的断言方法。例如:相等\不相等,包含\不包含,True\False的断言方法等
3、提供丰富的日志:
当测试用例执行失败时能抛出清晰的失败原因,当所有用例执行完成之后能提供丰富的执行结果。例如,总执行时间、失败用例数、成功用例数等
测试用例类,必须继承unittest.TestCase,测试用例函数必须以test开通
class Count:
def __init__(self, x, y):
self.x = int(x)
self.y = int(y)
def Plus(self, ):
plus = self.x + self.y
return plus
def Subtract(self):
subtract = self.x - self.y
return subtract
def Multiply(self):
multiply = self.x * self.y
return multiply
def Divide(self):
divide = self.x / self.y
return divide
if __name__ == '__main__':
count = Count(6, 3)
print(count.Plus())
print(count.Subtract())
print(count.Multiply())
print(count.Divide())
如果想测试上述代码:
import unittest
from 单元测试 import Count
class TestAddCount(unittest.TestCase):
def setUp(self) -> None:
print("测试开始")
def test_add_case(self):
count = Count(5, 2)
self.assertEqual(count.Plus(), 7)
def test_subtract_case(self):
count = Count(5, 2)
self.assertEqual(count.Subtract(), 3)
def test_multiply_case(self):
count = Count(5, 2)
self.assertEqual(count.Multiply(), 10)
def test_divide_case(self):
count = Count(5, 2)
self.assertEqual(count.Divide(), 2.5)
def tearDown(self) -> None:
print("测试结束")
if __name__ == '__main__':
unittest.main()
单元测试重要的概念
使用unittest前需要了解该框架的五个概念,即test case,test suite,testLoader,test runner,test fixture
1、Test Case:
一个TestCase的实例就是一个测试用例。什么是测试用例呢?就是一个完整的测试流程。包括测试前准备环境的搭建(SetUP)、实现测试过程的代码(run),以及测试后环境的还原(tearDown)。单元测试(Unittest)的本质也就在这里,一个测试用例就是一个完整的测试单元,通过运行这个测试单元,可以对某一个功能进行验证
2、Test Suite:
一个功能的验证往往需要多个测试用例,可以把多个测试用例集合在一起执行,这就产生了测试套件TestSuite的概念。TestSuite用来组装单个测试用例。可以通过addTest加载TestCase到TestSuite中,从而返回一个TestSuite实例。而且TestSuite也可以嵌套TestSuite
3、Test Runner:
测试的执行也是单元测试中非常重要的一个概念,一般单元测试框架中都会提供丰富的执行策略和执行结果。在Unittest单元测试框架中,通过TextTestRunner类提供的run()方法来执行test suite/test case。test runner可以使用图形界面、文本界面,或返回一个特殊的值等方式来表示测试执行的结果
4、Test Fixture:
对一个测试用例环境的搭建和销毁,就是一个Fixture,通过覆盖TestCase的setUP()和tearDown()方法来实现。这有什么用呢?比如说在这个测试用例中需要访问数据库,那么可以在setUP()中建立数据库连接来进行初始化,在tearDown()中清除数据库产生的数据,然后关闭连接
import unittest
from 单元测试 import Count
class TestAddCount(unittest.TestCase):
def setUp(self) -> None:
print("测试开始")
def test_add_case(self):
count = Count(5, 2)
print("测试加法")
self.assertEqual(count.Plus(), 7)
def test_subtract_case(self):
count = Count(5, 2)
print("测试减法")
self.assertEqual(count.Subtract(), 3)
def test_multiply_case(self):
count = Count(5, 2)
print("测试乘法")
self.assertEqual(count.Multiply(), 10)
def test_divide_case(self):
count = Count(5, 2)
print("测试除法")
self.assertEqual(count.Divide(), 2.5)
def tearDown(self) -> None:
print("测试结束")
if __name__ == '__main__':
#构造测试集
suite = unittest.TestSuite()
suite.addTest(TestAddCount("test_multiply_case"))
# 执行测试
runner = unittest.TextTestRunner()
runner.run(suite)
1、从上面的例子中我们去掉了main()方法,采用构造测试集的方法来加载与运行测试用例,实现了有选择的执行测试用例
2、上面代码的执行过程:首先调用unittest框架的TestSuite()类来创建测试套件(实例化),通过它提供的addTest()方法来添加测试用例(test_division_case)。接着调用unittest框架的TextTestRunner()类(实例化),通过它下面的run()方法来运行suite所组装的测试用例
2、内置断言
断言就是用来校验预期结果和实际结果的,正常判断的话,我们需要写if条件,unittest内部封装了很多断言,可以使用它来校验,就不用写if了。 如果有多条断言,有一条执行失败,那它后面的断言都会不再执行了。
方法 | 作用 |
---|---|
self.assertEqual(a, b,msg=None) | a ==b |
self.assertNotEqual(a, b,msg=None) | a !=b |
self.assertTrue(x) | bool(x) is True |
self.assertFalse(x) | Bool(x) is False |
self.assertIs(a, b,msg=None) | a is b |
self.assertIsNot(a, b,msg=None) | a is not b |
self.assertIsNone(x) | x is None |
self.assertIsNotNone(x) | x is not None |
self.assertIn(a, b,msg=None) | a in b |
self.assertNotIn(a, b,msg=None) | a not in b |
self.assertIsInstance(a, b,msg=None) | isinstance(a,b,msg=None) |
self.assertNotIsInstance(a, b,msg=None) | not isinstance(a,b,msg=None) |
self.assertCountEqual(a,b,msg=None) | 判断传入的两个元素数量是否一样,比如两个list的长度是否一致 |
3、测试用例跳过
我们在执行case的时候,有一些case在某种条件下不需要被执行,那在unittest中也提供了这种功能,使用unittest.skip或者unittest.skipIf装饰测试用例即可。可以装饰函数,也可以装饰类 skip是无条件跳过,skipIf可以传一个条件,在某种条件下可以被执行。
import unittest
import sys
class TestCase(unittest.TestCase):
@unittest.skip("无条件跳过")
# 无条件跳过,就是不执行,就是玩,需要传一个字符串,说明跳过原因
def test_print(self):
print("test_print")
@unittest.skipIf(sys.platform in ["win32", "darwin"], "不是在服务器上,不执行该条case")
# if需要传2个参数,第一个参数是一个布尔值,true或者false,第二个参数是跳过的原因
# 带条件的跳过,比如说当运行在本地的时候,不执行这条case,本地电脑一般是mac或者Windows,这里判断一下
def test_hello(self):
print("test_hello")
def test_normal(self):
print("我是一个不跳过的case")
@unittest.skip("跳过整个类") #也可以把装饰器加在类上面,跳过整个测试类
class TestCase2(unittest.TestCase):
def test_mygod(self):
print("我是一个不跳过的case")
def test_my(self):
print("我是一个不跳过的case")
if __name__ == '__main__':
unittest.main()
4、参数化
参数一样,值不一样的情况下,我们只写一个函数就可以了,可以使用参数化,传入多个参数,unittest可以自动根据参数数据调用测试用例,参数化需要安装parameterized模块,它使用的时候需要传入一个二维数组,二维数组里面的元素个数要和函数参数个数一致
[
](https://blog.csdn.net/qq_39314932/article/details/91612529)
安装parameterized模块
pip install parameterized
import unittest
import parameterized
class TestCalc(unittest.TestCase):
@parameterized.parameterized.expand((
[1, 1, 2, ],
[2, 3, 5, ],
[3, 3, 6, ],
))
def test_param_not_msg(self, a, b,c): # 传入预期结果和入参
self.assertEqual(a+b, c)
if __name__ == '__main__':
unittest.main() # 运行case
import unittest
import parameterized
def calc(seq):
try:
ret = eval(seq)
except:
return False
return ret
class TestCalc(unittest.TestCase):
@parameterized.parameterized.expand((
["1+1", 1, "加法", "case失败提示"],
["1*2", 2, "乘法", "case失败提示"],
["1-1", 0, "减法", "case失败提示"],
["1/1", 1, "除法", "case失败提示"],
))
def test_param(self, expression, except_result, desc, msg):#有用例描述和需要定义case失败原因的
self._testMethodDoc = desc # 用例描述,如果需要指定的话,加上它
ret = calc(expression)
self.assertEqual(except_result, ret, msg)
5、用例前置、后置操作
实际在用例执行的时候,可能有一些前置条件,执行case之前要打开一下文件,连接一下数据库,初始化数据,或者case执行完之后需要执行数据清理的操作,就需要用到前置操作和后置操作。
函数名 | 作用 | 备注 |
---|---|---|
setUpClass | 所有case运行前执行 | 是一个类方法,需要使用@classmethod装饰 |
tearDownClass | 所有case运行后执行 | 是一个类方法,需要使用@classmethod装饰 |
setUp | 每条case运行前执行 | |
tearDown | 每条case运行后执行 |
import unittest
class TestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("所有case运行之前会执行这里的代码")
@classmethod
def tearDownClass(cls):
print("所有case运行之后会执行这里的代码")
def setUp(self):
print("每个case执行之前会执行这里的代码")
def tearDown(self):
print("每个case执行之后会执行这里的代码")
def test_print(self):
print("我是一个没有感情的测试函数,只是用我print,测其他的")
def test_output(self):
print("我也是一个没有感情的测试函数,只是用我print,测其他的东西")
if __name__ == '__main__':
unittest.main()
6、用例查找
前面运行用例的时候都是在一个py文件里面的,实际我们写自动化case的时候不会把所有的case都写到一个py文件里,都是在多个py文件里面,所以需要使用测试用例查找功能
目录结构如下,cases目录下有3个py文件,里面各写几条case,run.py查找test_cases目录下的case,运行
# run.py
import unittest
test_suite = unittest.defaultTestLoader.discover('test_cases','test*.py') #指定目录,查找case,查找以test开头的py文件,返回用例集合
test_runner = unittest.TextTestRunner()#实例化test_runner
test_runner.run(test_suite)#使用test_runner运行case
7、产生测试报告
测试报告需要使用第三方模块,HTMLTestRunner.py,原版的HTMLTestRunner产生的报告不好看,没这个好看,所以这里用的是别人修改过好看的。
使用时下载下来即可。
HTMLTestRunnerNew.py
把HTMLTestRunnerNew.py放在跟cases同层目录中
import unittest
import HTMLTestRunnerNew
test_suite = unittest.defaultTestLoader.discover('test_cases','test*.py') #指定目录,查找case,查找以test开头的py文件,返回用例集合
f = open("report.html",'wb') #打开存放测试报告的html文件
test_runner = HTMLTestRunnerNew.HTMLTestRunner(f,title="测试报告",description="测试报告描述")
case_result = test_runner.run(test_suite) #执行case
print("用例总数",test_suite.countTestCases())
print("成功个数",case_result.success_count)
print("错误个数",case_result.error_count) #指的是case运行报错了,不是断言不通过的
print("失败个数",case_result.failure_count)#指的是case失败的个数,断言不通过的
print("跳过的个数",len(case_result.skipped)) #跳过多少个
print("开始运行时间",test_runner.startTime)
print("结束运行时间",test_runner.stopTime)
f.close()
pytest单元测试框架
原文链接:https://blog.csdn.net/qq_39314932/article/details/91612529
原文链接:https://www.yuque.com/mrwei-vhjbd/lmzics/gthqac