unittest框架从编写测试脚本到生成测试报告

目录

1、什么是UnitTest框架

2、为什么使用UnitTest框架

3、UnitTest核心要素

4、UnitTest使用流程

5、unittest断言

6、TestCase测试用例

7、TestSuit测试套件

8、TestLoader测试加载

8.1、discover加载

8.2、defaultTestLoader通过类名、模块名加载

8.3、小结

9、Fixture测试夹具

10、参数化

10.1、环境准备

10 .2、通过元组列表实现参数化

10.3、使用json数据参数化

11、跳过

12、测试报告


1、什么是UnitTest框架

​ 概念:UnitTest是Python自带的一个单元测试框架,可以用它来做单元测试

2、为什么使用UnitTest框架

  • 能够组织多个用例去执行

  • 提供丰富的断言方法

  • 能够生成测试报告

3、UnitTest核心要素

  • TestCase(测试用例)

  • TestSuite(测试套件,把多个TestCase集成到一个测试TestSuite)

  • TextTestRunner(执行测试用例)

  • Fixture(测试夹具)

4、UnitTest使用流程

第一步:导入unittest模块

第二步:创建一个类,这个类必须继承自UnitTest.TestCase类

第三步:类中每个方法代表一个测试用例,方法名必须以test开头

第四步:将测试用例加入到TestSuite

第五步:使用TextTestRunner执行

5、unittest断言

    让程序代替人判断程序执行结果是否符合预期结果的过程,需要在测试用例中对测试结果进行断言。

assertEqual(a, b, msg=None)       # a == b    断言a和b是否相等,相等则测试用例通过
assertNotEqual(a, b,msg=None) # a != b 断言a和b是否相等,不相等则测试用例通过
assertTrue(x,msg=None) # x is True 断言x是否True,是True则测试用例通过  
assertFalse(x,msg=None)  # x is False 断言x是否False,是False则测试用例通过
assertIn(a, b,msg=None) # a in b 断言a是否在b中,在b中则测试用例通过  
assertNotIn(a, b,msg=None)    # a not in b 断言a是否在b中,不在b中则测试用例通过
  • assertEqual(参数1,参数2,msg=None)

    • 如果参数1和参数2相等,断言成功,否则断言失败

    • 参数1存放实际结果,参数2存放预期结果

    • msg默认信息为None,可以给msg指定信息,当断言失败时,会作为错误信息返回

6、TestCase测试用例

        在编写测试用例之前,我们需要创建一个存放测试用例的类,导入unittest,并且要继承unittest.TestCase类,该类中的方法必须要以test开头

        下面创建两个测试类,StringTest和NumberTest,分别写了两个测试方法

StringTest类

import unittest
class StringTest(unittest.TestCase):
    def test_Method1(self):
        self.assertIn("hello","hello world")
    def test_Method2(self):
        self.assertEqual("H","Happy")

NumberTest类

import unittest
class NumberTest(unittest.TestCase):
    def test_funa(self):
        self.assertEqual(1,2,"两个参数不相等")
    def test_funb(self):
        self.assertEqual(2,2)

        这两个类可以直接运行,直接在该类中,右键点击Run Unittests for 模块名.类名,就可以直接查看测试结果,或者也可以加入如下代码,直接调用unittest.main()来执行

if __name__ == "__main__":
    unittest.main()

       单独执行NumberTest类,一共两条测试用例,一条通过,一条不通过,并打印了错误信息

7、TestSuit测试套件

       上面介绍了如何执行单个测试类里面的测试用例,但是测试是不可能只写一个测试类,当需要执行多个测试类中的测试方法时,需要用到测试套件,把测试用例组装起来。

        1、实例化:suite = unittest.TestSuit() (suite:为TestSuit实例化的名称)

        2、添加单个用例:

suit.addTest(ClassName("MethodName")) (ClassName:为类名 MethodName:为方法名)

        3、添加多个测试用例,参数为列表suit.addTests([ClassName("MethodName1"),ClassName("MethodName2")]) 

​        4、TestSuit需要配合TextTestRunner才能被执行,

还以StringTest和NumberTest为例,介绍TestSuit的使用

#导入测试类
from frame_unittest.numberTest import NumberTest
from frame_unittest.stringTest import StringTest
import unittest
if __name__ == "__main__":
    suit = unittest.TestSuite() #实例化测试套件
    suit.addTest(NumberTest("test_funa"))  #单个添加测试用例
    suit.addTest(NumberTest("test_funb"))
    suit.addTests([StringTest("test_Method1"),StringTest("test_Method2")])  #添加多个测试用例
    runner = unittest.TextTestRunner() #实例化TextTestRunner
    runner.run(suit)  #执行测试用例

8、TestLoader测试加载

        作用和TestSuit作用一样,组装用例代码,同样也需要使用TextTestRunner去执行,  用来加载TestCase到TestSuit中,即加载满足条件的测试用例,测试用例封装成测试套件

8.1、discover加载

        使用unnitest.TestLoader,通过该类下面的discover()方法自动搜索指定开头的.py文件,并查找到的测试用例组装到测试套件

        用法:suit = unittest.TestLoader().discover(test_dir,pattern='test*.py')

import unittest
import os
if __name__ == "__main__":

    path = os.path.dirname(__file__) #获取当前文件所在目录的路径
    suit = unittest.TestLoader().discover(path,"*Test.py") #加载当前目录下,以Test.py结尾的模块
    runner = unittest.TextTestRunner() #实例化TextTestRunner
    runner.run(suit)  #执行测试用例

8.2、defaultTestLoader通过类名、模块名加载

           defaultTestLoader可以通过类名和模块名的方式加载,与TestLoader中discover不同的点在于,defaultTestLoader还需要把加载的内容添加到TestSuit中,discover则不用。执行都需要使用TextTestRunner去执行

        下面的测试用例还是借用上述的StringTest和NumberTest,使用类名和模块名两种当时执行测试用例

      unittest.defaultTestLoader.loadTestsFromTestCase(类名)  添加一个类
import unittest
from frame_unittest.numberTest import NumberTest
from frame_unittest.stringTest import StringTest
#加载的是类名
nt =unittest.defaultTestLoader.loadTestsFromTestCase(StringTest)
st =unittest.defaultTestLoader.loadTestsFromTestCase(NumberTest)
suit = unittest.TestSuite([st,nt])
unittest.TextTestRunner().run(suit)

unittest.defaultTestLoader.loadTestsFromModule(模块名)  添加一个模块
import unittest
#导入需要加载的模块
from frame_unittest import numberTest
from frame_unittest import stringTest
names =[numberTest,stringTest]
modules=[]
#加载模块名
for name in names:
    module=unittest.defaultTestLoader.loadTestsFromModule(name)
    modules.append(module)
suit = unittest.TestSuite(modules)
unittest.TextTestRunner().run(suit)

8.3、小结

        组装测试套件有多种方式

        方式一:添加单个测试类的单个测试方法 组装到测试套件中  addTest()

        方式二:搜索该路径下所有符合命名规则的模块组装到测试套件中 discover()

        方式三:通过加载类名的方式组装到测试套件   loadTestsFromTestCase()

        方式四:用过加载模块名的方式组装到测试套件  loadTestsFromModule()

  • 所有的TestCase最终都是用TextTestRunner来执行的

  • TextTestRunner执行的是TestSuit

  • 一个TestSuit中有多个TestCase

9、Fixture测试夹具

        Fixture是一个概述,对一个测试用例环境的初始化和销毁就是一个Fixture

可以在测试用例执行之前调用指定的函数,在测试用例执行之后调动指定的函数

Fixture控制级别

  • 方法级别

    • 每个方法执行前和执行后都自动调用函数

  • 类级别

    • 不管类中有多少方法,一个类执行前后都自动调用函数

  • 模块级别

    • 不管一个模块(一个模块就是一个py文件)中有多少类,模块执行前后自动调用函数

1、方法级

在TestCase,也就是测试用例所在的class中定义方法

def setUp(self)当测试用例执行前,自动被调用

def tearDown(self)当测试用例执行后,自动被调用

如果一个TestCase中有多个测试用例,那么setUp和tearDown就会被自动调用多次

import unittest
def add(a,b):
    sum = a + b
    return sum
​
class my_test(unittest.TestCase):
    def setUp(self):
        print("setup被调用了")
    def tearDown(self):
        print("teardown被调用了")
    def test_001(self):
        print(add(4,5))
​
    def test_002(self):
        print(add(0,6))
​
    def test_003(self):
        print(add(2,4))

2、类级别:

  • 不管类中有多少个方法,一个类开始的时候自动调用函数,结束之后自动调用函数

  • 类级别的fixture一定要有类方法@classmethod

import unittest
def add(a,b):
    return a+b
​
​
class my_test_demo(unittest.TestCase):
​
    @classmethod
    def setUpClass(cls):
        print("这个是类级别测试开始")
    @classmethod
    def tearDownClass(cls):
        print("这个是类级别的结束")
​
    def test_01(self):
        print(add(3,2))
    def test_02(self):
        print(add(1,2))

3、模块级别:

  • 不管py文件有多少个类,以及类中有多少个方法,只自动执行一次

  • def setUpModule() 在py文件开始的时候自动调用

  • def tearDownModule()在文件结束的时候自动调用

import unittest
def add(a,b):
    return a+b
​#模块级别
def setUpModule():
    print("setUpModule自动调用了")
​​#模块级别
def tearDownModule():
    print("tearDownModule调用结束了")
​
​
class my_test_demo(unittest.TestCase):
​    #类级别
    @classmethod
    def setUpClass(cls):
        print("这个是类级别测试开始")
    @classmethod
    def tearDownClass(cls):
        print("这个是类级别的结束")
​
    def test_01(self):
        print(add(3,2))
    def test_02(self):
        print(add(1,2))
​    #方法级别
    def setUp(self):
        print("setup被调用了")
    def tearDown(self):
        print("teardown被调用了")

10、参数化

        测试用例中使用参数化的场景:​多个测试用例代码相同,只是测试数据不同,预期结果不同,可以把多个测试用例通过参数化技术合并为一个

       通过参数的方式来传递数据,从而实现数据和脚本分离。并且可以实现用例的重复执行。(在书写用例方法的时候,测 试数据使用变量代替,在执行的时候进行据说传递)
    unittest 测试框架,本身不支持参数化,但是可以通过安装unittest扩展插 件 parameterized 来实现。

10.1、环境准备

因为参数化的插件 不是 unittest 自带的,所以想要使用 需要进行安装

Python 中 包(插件,模块) 的安装,使用 pip 工具, 在终端(cmd)中执行
pip install parameterized
pip install -i https://pypi.douban.com/simple/ parameterized
pip list     # 查看安装的所有的插件

10 .2、通过元组列表实现参数化

​      定义一个加法函数,模块名为tools

def add(a,b):
    return a+b

    定义一个测试类,模块名为TestAdd.py

from parameterized import parameterized  #导入parameterized
import unittest

from frame_unittest.tools import add
#使用data存放一组测试数据
data = [(1, 1, 2), (1, 2, 3), (2, 3, 5), (4, 5, 9)]


class Test_Add(unittest.TestCase):
    @parameterized.expand(data) #data数据与测试用例的参数一一对应
    def test(self,a,b,expect):
        self.assertEqual(add(a,b),expect)
if __name__ == "__main__":
    unittest.main()

10.3、使用json数据参数化

JSON格式一

定义一个data.json

[
  [1, 1, 2],
  [1, 2, 3],
  [2, 3, 5],
  [4, 5, 9],
  [10, 20, 30]
]

定义一个TestAdd_Method.py

import unittest
import os
import json
from parameterized import parameterized
from frame_unittest.tools import add
#获取当前文件所在目录
path = os.path.abspath(os.path.dirname(os.getcwd()))
#定义一个读取json数据的方法
def read_data():
    with open(path+"/data/add.json",mode="r",encoding="utf-8") as file:
        data = json.load(file)
        return data
class TestMethod(unittest.TestCase):
    @parameterized.expand(read_data())
    def test_method(self,a,b,expect):
        self.assertEqual(add(a,b),expect)

JSON格式二

定义一个data1.json

[
  { "a": 1, "b": 2, "expect": 3 },
  { "a": 11, "b": 22, "expect": 33 },
  { "a": 12, "b": 23, "expect": 35 },
  { "a": 14, "b": 25, "expect": 39 }
]

建立一个模块名为addmethod.py的测试类

import os
import unittest
import json
from frame_unittest.tools import add
from parameterized import parameterized
​
def readdata():
    path = os.path.abspath(os.path.dirname(os.getcwd()))
    with open(path+"/data/add1.json",mode="r",encoding="utf-8") as file:
        datas = json.load(file)  #读取json文件
        list_data = []
        for data in datas:
            a = data.get("a")
            b = data.get("b")
            expect = data.get("expect")
            list_data.append((a,b,expect))
    return list_data #返回[(),(),()...]的数据格式
             #list_data.append(tuple(data.values()))
​
class AddMethod(unittest.TestCase):
    @parameterized.expand(readdata())
    def test_add(self,a,b,expect):
        self.assertEqual(add(a,b),expect)

11、跳过

        跳过:对于一些未完成的或者不满足测试条件的测试函数和测试类,可以跳过执行(简单来说, 不想执行的测试方法,可以 设置为跳过)
        - 直接将测试函数标记成跳过
                @unittest.skip('跳过的原因')
        - 根据条件判断测试函数是否跳过
                @unittest.skipIf(判断条件, reason='原因') # 判断条件为 True, 执行跳过
import unittest
version = 35
class TestSkip(unittest.TestCase):
    @unittest.skip('跳过此条不执行')
    def test_1(self):
        print('方法一')
    @unittest.skipIf(version >= 30, '版本号大于等于 30, 测方法不用执行')
    def test_2(self):
        print('方法二')
    def test_3(self):
        print('方法三')
if __name__ == '__main__':
    unittest.main()

执行结果

============================= test session starts =============================
collecting ... collected 3 items

mm.py::TestSkip::test_1 SKIPPED (跳过此条不执行)                         [ 33%]
Skipped: 跳过此条不执行

mm.py::TestSkip::test_2 SKIPPED (版本号大于等于 30, 测方法不用执行)      [ 66%]
Skipped: 版本号大于等于 30, 测方法不用执行

mm.py::TestSkip::test_3 PASSED                                           [100%]方法三


======================== 1 passed, 2 skipped in 0.02s =========================

12、测试报告

使用第三方的报告模版,生成报告 HTMLTestReport, 本质是 TestRunner

- 安装

pip install -i https://pypi.douban.com/simple/ HTMLTestReport

- 使用

  1. 导包 unittest、HTMLTestReport

  2. 组装用例(套件, loader )

  3. 使用 HTMLTestReport 中的 runner 执行套件

  4. 查看报告

以最开始的stringtest和numbertest为例

import unittest
from htmltestreport import HTMLTestReport
import os
if __name__ == "__main__":
    path = os.path.dirname(__file__)
    suit = unittest.TestLoader().discover(path+"/","*test.py")
    # runner = HTMLTestReport(报告的文件路径后缀.html, 报告的标题, 其他的描述信息)
    runner = HTMLTestReport("report.html","数据类型测试报告")
    runner.run(suit)

生成的测试报告如下

猜你喜欢

转载自blog.csdn.net/xiatian22/article/details/129081958