Unittest单元测试框架

1、unittest是什么

unittest是python内置的单元测试框架,具备编写用例、组织用例、执行用例、输出报告等自动化框架的条件

2、单元测试框架的优点

一般来说不用单元测试框架也能编写单元测试,因为单元测试本身就是通过一段代码去验证另一段代码,所以不用单元测试框架也能编写单元测试。只是使用框架时会有更多的优点
1、提供用例组织与执行:
当测试用例达到成百上千条时,就产生了扩展性与维护性等问题,此时就需要考虑用例的规范与组织问题了。单元测试框架便能很好的解决这个问题

2、提供丰富的比较方法:
不论是功能测试还是单元测试,在用例完成之后都需要将实际结果与预期结果进行比较(断言),从而断定用例是否执行通过。单元测试框架一般会提供丰富的断言方法。例如:相等\不相等,包含\不包含,True\False的断言方法等

3、提供丰富的日志:
当测试用例执行失败时能抛出清晰的失败原因,当所有用例执行完成之后能提供丰富的执行结果。例如,总执行时间、失败用例数、成功用例数等

测试用例类,必须继承unittest.TestCase,测试用例函数必须以test开通

  1. class Count:
  2. def __init__(self, x, y):
  3. self.x = int(x)
  4. self.y = int(y)
  5. def Plus(self, ):
  6. plus = self.x + self.y
  7. return plus
  8. def Subtract(self):
  9. subtract = self.x - self.y
  10. return subtract
  11. def Multiply(self):
  12. multiply = self.x * self.y
  13. return multiply
  14. def Divide(self):
  15. divide = self.x / self.y
  16. return divide
  17. if __name__ == '__main__':
  18. count = Count(6, 3)
  19. print(count.Plus())
  20. print(count.Subtract())
  21. print(count.Multiply())
  22. print(count.Divide())

如果想测试上述代码:

  1. import unittest
  2. from 单元测试 import Count
  3. class TestAddCount(unittest.TestCase):
  4. def setUp(self) -> None:
  5. print("测试开始")
  6. def test_add_case(self):
  7. count = Count(5, 2)
  8. self.assertEqual(count.Plus(), 7)
  9. def test_subtract_case(self):
  10. count = Count(5, 2)
  11. self.assertEqual(count.Subtract(), 3)
  12. def test_multiply_case(self):
  13. count = Count(5, 2)
  14. self.assertEqual(count.Multiply(), 10)
  15. def test_divide_case(self):
  16. count = Count(5, 2)
  17. self.assertEqual(count.Divide(), 2.5)
  18. def tearDown(self) -> None:
  19. print("测试结束")
  20. if __name__ == '__main__':
  21. unittest.main()

单元测试重要的概念

使用unittest前需要了解该框架的五个概念,即test case,test suite,testLoader,test runner,test fixture

1、Test Case:

  1. 一个TestCase的实例就是一个测试用例。什么是测试用例呢?就是一个完整的测试流程。包括测试前准备环境的搭建(SetUP)、实现测试过程的代码(run),以及测试后环境的还原(tearDown)。单元测试(Unittest)的本质也就在这里,一个测试用例就是一个完整的测试单元,通过运行这个测试单元,可以对某一个功能进行验证

2、Test Suite:

  1. 一个功能的验证往往需要多个测试用例,可以把多个测试用例集合在一起执行,这就产生了测试套件TestSuite的概念。TestSuite用来组装单个测试用例。可以通过addTest加载TestCaseTestSuite中,从而返回一个TestSuite实例。而且TestSuite也可以嵌套TestSuite

3、Test Runner:

  1. 测试的执行也是单元测试中非常重要的一个概念,一般单元测试框架中都会提供丰富的执行策略和执行结果。在Unittest单元测试框架中,通过TextTestRunner类提供的run()方法来执行test suite/test casetest runner可以使用图形界面、文本界面,或返回一个特殊的值等方式来表示测试执行的结果

4、Test Fixture:

  1. 对一个测试用例环境的搭建和销毁,就是一个Fixture,通过覆盖TestCasesetUP()和tearDown()方法来实现。这有什么用呢?比如说在这个测试用例中需要访问数据库,那么可以在setUP()中建立数据库连接来进行初始化,在tearDown()中清除数据库产生的数据,然后关闭连接
  1. import unittest
  2. from 单元测试 import Count
  3. class TestAddCount(unittest.TestCase):
  4. def setUp(self) -> None:
  5. print("测试开始")
  6. def test_add_case(self):
  7. count = Count(5, 2)
  8. print("测试加法")
  9. self.assertEqual(count.Plus(), 7)
  10. def test_subtract_case(self):
  11. count = Count(5, 2)
  12. print("测试减法")
  13. self.assertEqual(count.Subtract(), 3)
  14. def test_multiply_case(self):
  15. count = Count(5, 2)
  16. print("测试乘法")
  17. self.assertEqual(count.Multiply(), 10)
  18. def test_divide_case(self):
  19. count = Count(5, 2)
  20. print("测试除法")
  21. self.assertEqual(count.Divide(), 2.5)
  22. def tearDown(self) -> None:
  23. print("测试结束")
  24. if __name__ == '__main__':
  25. #构造测试集
  26. suite = unittest.TestSuite()
  27. suite.addTest(TestAddCount("test_multiply_case"))
  28. # 执行测试
  29. runner = unittest.TextTestRunner()
  30. 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可以传一个条件,在某种条件下可以被执行。

  1. import unittest
  2. import sys
  3. class TestCase(unittest.TestCase):
  4. @unittest.skip("无条件跳过")
  5. # 无条件跳过,就是不执行,就是玩,需要传一个字符串,说明跳过原因
  6. def test_print(self):
  7. print("test_print")
  8. @unittest.skipIf(sys.platform in ["win32", "darwin"], "不是在服务器上,不执行该条case")
  9. # if需要传2个参数,第一个参数是一个布尔值,true或者false,第二个参数是跳过的原因
  10. # 带条件的跳过,比如说当运行在本地的时候,不执行这条case,本地电脑一般是mac或者Windows,这里判断一下
  11. def test_hello(self):
  12. print("test_hello")
  13. def test_normal(self):
  14. print("我是一个不跳过的case")
  15. @unittest.skip("跳过整个类") #也可以把装饰器加在类上面,跳过整个测试类
  16. class TestCase2(unittest.TestCase):
  17. def test_mygod(self):
  18. print("我是一个不跳过的case")
  19. def test_my(self):
  20. print("我是一个不跳过的case")
  21. if __name__ == '__main__':
  22. unittest.main()

4、参数化

参数一样,值不一样的情况下,我们只写一个函数就可以了,可以使用参数化,传入多个参数,unittest可以自动根据参数数据调用测试用例,参数化需要安装parameterized模块,它使用的时候需要传入一个二维数组,二维数组里面的元素个数要和函数参数个数一致

[

](https://blog.csdn.net/qq_39314932/article/details/91612529)
安装parameterized模块
pip install parameterized

  1. import unittest
  2. import parameterized
  3. class TestCalc(unittest.TestCase):
  4. @parameterized.parameterized.expand((
  5. [1, 1, 2, ],
  6. [2, 3, 5, ],
  7. [3, 3, 6, ],
  8. ))
  9. def test_param_not_msg(self, a, b,c): # 传入预期结果和入参
  10. self.assertEqual(a+b, c)
  11. if __name__ == '__main__':
  12. unittest.main() # 运行case
  1. import unittest
  2. import parameterized
  3. def calc(seq):
  4. try:
  5. ret = eval(seq)
  6. except:
  7. return False
  8. return ret
  9. class TestCalc(unittest.TestCase):
  10. @parameterized.parameterized.expand((
  11. ["1+1", 1, "加法", "case失败提示"],
  12. ["1*2", 2, "乘法", "case失败提示"],
  13. ["1-1", 0, "减法", "case失败提示"],
  14. ["1/1", 1, "除法", "case失败提示"],
  15. ))
  16. def test_param(self, expression, except_result, desc, msg):#有用例描述和需要定义case失败原因的
  17. self._testMethodDoc = desc # 用例描述,如果需要指定的话,加上它
  18. ret = calc(expression)
  19. self.assertEqual(except_result, ret, msg)

5、用例前置、后置操作

实际在用例执行的时候,可能有一些前置条件,执行case之前要打开一下文件,连接一下数据库,初始化数据,或者case执行完之后需要执行数据清理的操作,就需要用到前置操作和后置操作。

函数名 作用 备注
setUpClass 所有case运行前执行 是一个类方法,需要使用@classmethod装饰
tearDownClass 所有case运行后执行 是一个类方法,需要使用@classmethod装饰
setUp 每条case运行前执行
tearDown 每条case运行后执行
  1. import unittest
  2. class TestCase(unittest.TestCase):
  3. @classmethod
  4. def setUpClass(cls):
  5. print("所有case运行之前会执行这里的代码")
  6. @classmethod
  7. def tearDownClass(cls):
  8. print("所有case运行之后会执行这里的代码")
  9. def setUp(self):
  10. print("每个case执行之前会执行这里的代码")
  11. def tearDown(self):
  12. print("每个case执行之后会执行这里的代码")
  13. def test_print(self):
  14. print("我是一个没有感情的测试函数,只是用我print,测其他的")
  15. def test_output(self):
  16. print("我也是一个没有感情的测试函数,只是用我print,测其他的东西")
  17. if __name__ == '__main__':
  18. unittest.main()

6、用例查找

前面运行用例的时候都是在一个py文件里面的,实际我们写自动化case的时候不会把所有的case都写到一个py文件里,都是在多个py文件里面,所以需要使用测试用例查找功能

目录结构如下,cases目录下有3个py文件,里面各写几条case,run.py查找test_cases目录下的case,运行

image.png

  1. # run.py
  2. import unittest
  3. test_suite = unittest.defaultTestLoader.discover('test_cases','test*.py') #指定目录,查找case,查找以test开头的py文件,返回用例集合
  4. test_runner = unittest.TextTestRunner()#实例化test_runner
  5. test_runner.run(test_suite)#使用test_runner运行case

7、产生测试报告

测试报告需要使用第三方模块,HTMLTestRunner.py,原版的HTMLTestRunner产生的报告不好看,没这个好看,所以这里用的是别人修改过好看的。
使用时下载下来即可。

HTMLTestRunnerNew.py
把HTMLTestRunnerNew.py放在跟cases同层目录中

  1. import unittest
  2. import HTMLTestRunnerNew
  3. test_suite = unittest.defaultTestLoader.discover('test_cases','test*.py') #指定目录,查找case,查找以test开头的py文件,返回用例集合
  4. f = open("report.html",'wb') #打开存放测试报告的html文件
  5. test_runner = HTMLTestRunnerNew.HTMLTestRunner(f,title="测试报告",description="测试报告描述")
  6. case_result = test_runner.run(test_suite) #执行case
  7. print("用例总数",test_suite.countTestCases())
  8. print("成功个数",case_result.success_count)
  9. print("错误个数",case_result.error_count) #指的是case运行报错了,不是断言不通过的
  10. print("失败个数",case_result.failure_count)#指的是case失败的个数,断言不通过的
  11. print("跳过的个数",len(case_result.skipped)) #跳过多少个
  12. print("开始运行时间",test_runner.startTime)
  13. print("结束运行时间",test_runner.stopTime)
  14. f.close()

image.png

pytest单元测试框架

原文链接:https://blog.csdn.net/qq_39314932/article/details/91612529
原文链接:https://www.yuque.com/mrwei-vhjbd/lmzics/gthqac