首页 > 其他分享 >Unittest框架的介绍及使用

Unittest框架的介绍及使用

时间:2024-08-03 21:06:34浏览次数:13  
标签:__ 框架 Unittest 介绍 用例 测试用例 test import unittest

介绍

基本概念

​ unittest是Python自带的一个单元测试框架, 它可以做单元测试, 也能用于编写和运行重复的测试工作。它给自动化测试用例开发和执行提供了丰富的断言方法, 判断测试用例是否通过, 并最终生成测试结果.

四大组件

  • test case:就是我们的测试用例,unittest中提供了一个基本类TestCase,可以用来创建新的测试用例,一个TestCase的实例就是一个测试用例;unittest中测试用例方法都是以test开头的,且执行顺序会按照方法名的ASCII值排序。
  • test fixure:测试夹具,用于测试用例环境的搭建和销毁。即用例测试前准备环境的搭建(SetUp前置条件),测试后环境的还原(TearDown后置条件),比如测试前需要登录获取token等就是测试用例需要的环境,运行完后执行下一个用例前需要还原环境,以免影响下一条用例的测试结果。
  • test suite:测试套件,用来把需要一起执行的测试用例集中放到一块执行,相当于一个篮子。我们可以使用TestLoader来加载测试用例到测试套件中。
  • test runner:用来执行测试用例的,并返回测试用例的执行结果。它还可以用图形或者文本接口,把返回的测试结果更形象的展现出来,如:HTMLTestRunner。

unittest的断言

​ 在python基础中,我们有讲过一个assert断言,使用方法比较简单,即assert 表达式, 提示信息,而unittest框架中也提供了一个自带的断言方式,主要有以下几种:

方法 检查
assertEqual(a, b,msg=None) a ==b
assertNotEqual(a, b) a !=b
assertTrue(x) bool(x) is True
assertFalse(x) Bool(x) is False
assertIs(a, b) a is b
assertIsNot(a, b) a is not b
assertIsNone(x) x is None
assertIsNotNone(x) x is not None
assertIn(a, b) a in b
assertNotIn(a, b) a not in b
assertIsInstance(a, b) isinstance(a,b)
assertNotIsInstance(a, b) not isinstance(a,b)

使用代码自动的判断预期结果和实际结果是否相符,如果断言失败即不通过就会抛出一个AssertionError断言错误,成功则标识为通过,以上几种方式都有一个共同点,就是都有一个msg参数(表中只列了一个,其实都有),默认是None,即msg = None,如果指定msg参数的值,则将该信息作为失败的错误信息返回。

案例:

import unittest


# assertEqual(预期结果,实际结果)
#   判断预期结果和实际结果是否相等,如果相等, 用例通过,如果不相等,抛出异常, 用例不通过

class TestAssert(unittest.TestCase):
    def test_equal_1(self):
        self.assertEqual(10, 10)  # 用例通过

    def test_assert_2(self):
        self.assertEqual(10, 11)  # 用例不通过

    def test_in(self):
        # self.assertIn('admin', '欢迎 admin 登录')  # 包含 通过
        # self.assertIn('admin', '欢迎 adminnnnnnnn 登录')  # 包含 通过
        # self.assertIn('admin', '欢迎 aaaaaadminnnnnnnn 登录')  # 包含 通过
        # self.assertIn('admin', '欢迎 adddddmin 登录')  # 不包含 不通过
        self.assertIn('admin', 'admin')  # 包含 通过

TestCase测试用例

​ 编写测试用例前,我们需要建一个测试类继承unittest里面的TestCase类,继承这个类之后我们才是真正的使用unittest框架去写测试用例,编写测试用例的步骤如下

  • 导入unittest模块
  • 创建一个测试类,并继承unittest.TestCase()
  • 定义测试方法,方法名必须以test_开头
  • 调用unittest.main()方法来运行测试用例,unittest.main()方法会搜索该模块下所有以test开头的测试用例方法,并自动执行

案例:

# 1.导包unittest
import unittest

# 2. 定义测试类, 只要继承 unittest.TestCase 类, 就是 测试类
class TestDemo(unittest.TestCase):

    # 3. 书写测试⽅法, ⽅法中的代码就是真正⽤例代码,
    def test_method1(self):
        print('测试⽅法⼀')

    def test_method2(self):
        print('测试⽅法⼆')

# 4. 执⾏
if __name__ == '__main__':
    unittest.main()

TestSuite 和 TestRunner

TestSuite测试套件

unittest.TestSuite()类来表示一个测试用例集,把需要执行的用例类或模块存到一起,常用的方法如下:

  • unittest.TestSuite()
    • addTest():添加单个测试用例方法
    • addTest([..]):添加多个测试用例方法,方法名存在一个列表
  • unittest.TestLoader()
    • loadTestsFromTestCase(测试类名):添加一个测试类
    • loadTestsFromModule(模块名):添加一个模块
    • discover(测试用例的所在目录):指定目录去加载,会自动寻找这个目录下所有符合命名规则的测试用例

执行步骤:

  • 导包 unittest
  • 实例化套件对象 unittest.TestSuite()
  • 添加⽤例⽅法

案例:

# 导包
import unittest

import test_register
from hm_02_testcase01 import TestDemo01
from hm_02_testcase02 import TestDemo02

# 实例化套件对象
suite = unittest.TestSuite()

# 方式一:添加多个测试用例
suite.addTest(TestDemo01('test_m1'))
suite.addTest(TestDemo01('test_m2'))
suite.addTest(TestDemo02('test_m1'))
# suite.addTest(TestDemo02('test_m2'))

# 方式二:添加一个测试用例类
loader = unittest.TestLoader()  # 创建一个加载对象
suite.addTest(loader.loadTestsFromTestCase(TestDemo01))

# 方式三:指定测试用例的所在的目录路径,进行加载
loader = unittest.TestLoader()
suite.addTest(loader.discover(r"d:\learn\python"))

# 方式四,添加一个模块
loader = unittest.TestLoader()	# 创建一个加载对象
suite.addTest(loader.loadTestsFromModule(test_register))

# 实例化执行对象
runner = unittest.TextTestRunner()

# 执行对象执行套件对象 执行对象.run(套件对象)
runner.run(suite)

​ 通常我们使用方式三、四比较多,你可以根据实际情况来运用。其中方式三,还可以自定义匹配规则,默认是会寻找目录下test*.py文件,即所有以test开头命名的py文件,自定义如下:

loader = unittest.TestLoader()
# 匹配规则:所有以test_case开头的
suite.addTest(loader.discover(start_dir = r"d:\learn\python", pattern="test_case*.py"))		

TestRunner执行用例

​ test runner顾名思义就是用来执行测试用例的,并且可以生成相应的测试报告。测试报告有两种展示形式,一种是text文本,一种是html格式。

​ html格式的就是HTMLTestRunner了,HTMLTestRunner是 Python 标准库的 unittest 框架的一个扩展,它可以生成一个直观清晰的 HTML 测试报告。使用的前提就是要下载 HTMLTestRunner.py,下载完后放在python的安装目录下的scripts目录下即可。

​ text文本相对于html来说过于简陋,与控制台输出的没有什么区别,也几乎没有人使用,这里不作演示,使用方法是一样的。我们结合前面的测试套件来演示一下如何生成html格式的测试报告:

执行步骤:

  • 导包 unittest
  • 实例化套件对象 unittest.TestSuite()
  • 添加⽤例⽅法
    • 套件对象.addTest(测试类名('测试⽅法名'))
  • 实例化 执⾏对象 unittest.TextTestRunner()
  • 执⾏对象执⾏ 套件对象 执⾏对象.run(套件对象)

案例:

# 1. 导包  unittest
import unittest
from hm_02_testcase1 import TestDemo1
from hm_02_testcase2 import TestDemo2

# 2. 实例化套件对象 unittest.TestSuite()
suite = unittest.TestSuite()
# 3. 添加⽤例⽅法

# 3.1 套件对象.addTest(测试类名('测试⽅法名'))  # 建议复制
suite.addTest(TestDemo1('test_method1'))
suite.addTest(TestDemo1('test_method2'))
suite.addTest(TestDemo2('test_method1'))
suite.addTest(TestDemo2('test_method2'))

# 4. 实例化 执⾏对象 unittest.TextTestRunner()
runner = unittest.TextTestRunner()
# 5. 执⾏对象执⾏ 套件对象 执⾏对象.run(套件对象)
runner.run(suite)

案例2:

# run_test.py,与test_register.py、register.py同一目录下
import unittest
import test_register
from HTMLTestRunner import HTMLTestRunner

# 创建测试套件
suite = unittest.TestSuite()

# 通过模块加载测试用例
loader = unittest.TestLoader()
suite.addTest(loader.loadTestsFromModule(test_register))

# 创建测试运行程序启动器
runner = HTMLTestRunner(stream=open("report.html", "wb"),  # 打开一个报告文件,将句柄传给stream
                        tester="miki",                    # 报告中显示的测试人员
                        description="注册接口测试报告",        # 报告中显示的描述信息
                        title="自动化测试报告")                 # 报告的标题

# 使用启动器去执行测试套件里的用例
runner.run(suite)

相关参数说明:

  • stream:指定输出的方式

  • tester:报告中要显示的测试人员的名字

  • description:报告中要显示的面熟信息

  • title:测试报告的标题

  •   verbosity
    

    :表示测试报告信息的详细程度,一共三个值,默认是2

    • 0 (静默模式):你只能获得总的测试用例数和总的结果,如:总共100个 失败10 成功90
    • 1 (默认模式):类似静默模式,只是在每个成功的用例前面有个. 每个失败的用例前面有个F
    • 2 (详细模式):测试结果会显示每个测试用例的所有相关的信息

    运行完毕,你会发现你的项目目录下已经生成了一个report.html文件,在浏览器中打开,就可以查看测试报告了。

image-20240716210956367

TestFixure测试夹具

​ unittest的测试夹具有两种使用方式,一种是以测试方法为维度的setUp()tearDown(),一种是以测试类为维度的setUpClass()tearDownClass()。以注册功能为例,但这个注册代码比较简单,没有真正需要用到测试夹具的地方,因此这只是个用法演示。

# test_register.py
import unittest
from register import register  # 导入被测试的代码


class TestRegister(unittest.TestCase):
    """注册接口测试用例类"""

    def setUp(self):
        # 每条用例执行之前都会执行
        print("用例{}开始执行--".format(self))

    def tearDown(self):
        # 每条用例执行之后都会执行
        print("用例{}执行结束--".format(self))

    @classmethod  # 指明这是个类方法以类为维度去执行的
    def setUpClass(cls):
        # 整个测试用例类中的用例执行之前,会先执行此方法
        print("-----setup---class-----")

    @classmethod
    def tearDownClass(cls):
        # 整个测试用例类中的用例执行完之后,会执行此方法
        print("-----teardown---class-----")

    def test_register_success(self):
        """注册成功"""
        data = ("mikitest", "miki123", "miki123")  # 测试数据
        expected = {"code": 1, "msg": "注册成功!"}  # 预期结果
        result = register(*data)  # 把测试数据传到被测的代码,接收实际结果
        self.assertEqual(expected, result)  # 断言,预期和实际是否一致,一致即用例通过

    def test_username_isnull(self):
        """注册失败-用户名为空"""
        data = ("", "miki123", "miki123")
        expected = {"code": 0, "msg": "所有参数不能为空!"}
        result = register(*data)
        self.assertEqual(expected, result)


# 如果直接运行这个文件,需要使用unittest中的main函数来执行测试用例
if __name__ == '__main__':
    unittest.main()

运行结果:

Testing started at 22:19 ...
C:\software\python\python.exe "C:\Program Files\JetBrains\PyCharm Community Edition 2019.1.3\helpers\pycharm\_jb_unittest_runner.py" --path D:/learn/python/test_register.py
Launching unittests with arguments python -m unittest D:/learn/python/test_register.py in D:\learn\python
-----setup---class-----用例test_pwd1_not_pwd2 (test_register.RegisterTestCase)开始执行--
用例test_pwd1_not_pwd2 (test_register.RegisterTestCase)执行结束--
用例test_register_success (test_register.RegisterTestCase)开始执行--
用例test_register_success (test_register.RegisterTestCase)执行结束--
用例test_username_isnull (test_register.RegisterTestCase)开始执行--
用例test_username_isnull (test_register.RegisterTestCase)执行结束--
用例test_username_lt6 (test_register.RegisterTestCase)开始执行--
用例test_username_lt6 (test_register.RegisterTestCase)执行结束--
-----teardown---class-----

Ran 4 tests in 0.003s

OK

Process finished with exit code 0

参数化

​ 通过参数的方式来传递数据,从而实现数据和脚本分离。并且可以实现用例的重复执行。(在书写用例方法的时候,测试数据使用变量代替,在执行的时候进行据说传递)

​ unittest 测试框架,本身不支持参数化,但是可以通过安装unittest扩展插 件 parameterized 来实现。

环境准备

因为参数化的插件 不是 unittest 自带的,所以想要使用 需要进行安装
Python 中 包(插件,模块) 的安装,使用 pip 工具
pip install parameterized 
pip install -i https://pypi.douban.com/simple/  parameterized  
# 在终端(cmd)中执行

pip  list  # 查看安装的所有的插件

使用

  • 导包 from para... import para...
  • 修改测试方法,将测试方法中的测试数据使用 变量表示
  • 组织测试数据,格式 [(), (), ()], 一个元组就是一组测试数据
  • 参数化,在测试方法上方使用装饰器 @parameterized.expand(测试数据)
  • 运行(直接 TestCase 或者 使用 suite 运行)
import unittest
from parameterized import parameterized
from tools import add

data = [(1, 2, 3), (4, 7, 11), (1, 1, 2), (10, 10, 20), (34, 34, 68)]


class TestAdd(unittest.TestCase):

    @parameterized.expand(data)
    def test_add(self, a, b, expect):
        print(f'a:{a}  b:{b}  expect:{expect}')
        self.assertEqual(expect, add(a, b))


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

练习

将测试数据 定义为 json 文件, 读取 json 文件,完成参数化

  • json 文件
[
    [1, 1, 2],
    [1, 2, 3],
    [2, 3, 5],
    [4, 5, 9],
    [10, 20, 30]
 ]
  • 读取 json 文件
import json
def build_add_data():
    with open('add_data.json') as f:
        data = json.load(f)  # [[], [], []]  ---> [(), ()]
        return data
  • 代码文件
import unittest
from read_data import build_add_data
from tools import add


from parameterized import parameterized

data = [(1, 1, 2), (1, 2, 3), (2, 3, 5), (4, 5, 9)]


class TestAdd(unittest.TestCase):
    @parameterized.expand(build_add_data())
    def test_add(self, a, b, expect):
        print(f'a:{a}, b:{b}, expect:{expect}')
        self.assertEqual(expect, add(a, b))

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

测试报告

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

  • 安装
    pip install -i https://pypi.douban.com/simple/ HTMLTestReport
  • 使用
  1. 导包 unittest、HTMLTestReport
  2. 组装用例(套件, loader )
  3. 使用 HTMLTestReport 中的 runner 执行套件
  4. 查看报告
import unittest

from htmltestreport import HTMLTestReport
from hm_04_pa1 import TestAdd

# 套件

suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestAdd))

# 运行对象
# runner = HTMLTestReport(报告的文件路径后缀.html, 报告的标题, 其他的描述信息)
runner = HTMLTestReport('test_add_report.html', '加法用例测试报告', 'xxx')
runner.run(suite)

使用绝对路径

将来的项目是分目录书写的, 使用相对路径,可能会出现找不到文件的情况,此时需要使用 绝对路径

方法:

  1. 在项目的根目录,创建一个 Python 文件(app.py 或者 config.py)
  2. 在这个文件中 获取项目的目录,在其他代码中使用 路径拼接完成绝对路径的书写

image-20240716212313353

案例:

import os

# __file__ 特殊的变量,表示当前代码文件名
# path1 = os.path.abspath(__file__)
# print(path1)
# path2 = os.path.dirname(path1)
# print(path2)

# BASE_DIR = os.path.dirname(os.path.abspath(__file__))
BASE_DIR = os.path.dirname(__file__)


if __name__ == '__main__':
    print(BASE_DIR)

案例:

  1. 对登录函数进行测试, 登录函数 定义在 tools.py 中
  2. 在 case 目录中书写用例对login 函数进行测试, 使用断言
  3. 将 login 函数的测试数据定义在 json 文件中,完成参数化, data 目录中
  4. 生成测试报告 report 目录中
def login(username, password):
    if username == 'admin' and password == '123456':
        return '登录成功'
    else:
        return '登录失败'
  • 测试数据
[
]
{
    "desc": "正确的用户名和密码",
    "username": "admin",
    "password": "123456",
    "expect": "登录成功"
},
{
    "desc": "错误的用户名",
    "username": "root",
    "password": "123456",
    "expect": "登录失败"
},
{
    "desc": "错误的密码",
    "username": "admin",
    "password": "123123",
    "expect": "登录失败"
},
{
    "desc": "错误的用户名和密码",
    "username": "root",
    "password": "123123",
    "expect": "登录失败"
}
  • 读取测试数据的方法
def build_login_data():
    with open(BASE_DIR + '/data/login_data.json', encoding='utf-8') as f:
        data_list = json.load(f)  # [{}, {}] ---> [()]
        new_list = []
        for data in data_list:
            # 字典中的 desc 不需要
            username = data.get('username')
            password = data.get('password')
            expect = data.get('expect')
            new_list.append((username, password, expect))
            return new_list
  • 测试用例代码
import unittest

from common.read_data import build_login_data
from tools import login
from parameterized import parameterized


class TestLogin(unittest.TestCase):
    @parameterized.expand(build_login_data())
    def test_login(self, username, password, expect):
        print(f'username: {username}, password: {password}, expect: {expect}')
        self.assertEqual(expect, login(username, password))
  • Suite 报告代码
import unittest

from app import BASE_DIR
from case.test_login import TestLogin
from htmltestreport import HTMLTestReport

suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestLogin))

runner = HTMLTestReport(BASE_DIR + '/report/login_report.html', '登录测试报告', 'V1.0')
runner.run(suite)

跳过skip

​ 对于一些未完成的或者不满足测试条件的测试函数和测试类,可以跳过执行(简单来说, 不想执行的测试方法,可以 设置为跳过)

  • 直接将测试函数标记成跳过:

@unittest.skip('跳过的原因')

  • 根据条件判断测试函数是否跳过

@unittest.skipIf(判断条件, reason='原因') # 判断条件为 True, 执行跳过

import unittest

version = 29

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()

标签:__,框架,Unittest,介绍,用例,测试用例,test,import,unittest
From: https://www.cnblogs.com/kyrie-me/p/18341093

相关文章

  • 服务器性能分析软件「 WGCLOUD 」完整功能介绍
    WGCLOUD是一款国产开源的服务器状态性能监测软件,免费高效,轻量实用,部署简单,上手操作容易,颜值在线,更好的是它具有极低的资源占用WGCLOUD官网下载地址:www.wgstart.comWGCLOUD可以支持哪些操作平台,如下:Linux:Debian,RedHat,CentOS,Ubuntu,Fedora,SUSE,麒麟,统信(UOS),龙芯(mips)等Windows:W......
  • LLM场景下常用浮点数介绍
    在计算机中,浮点数的表示基于IEEE754标准,这是最广泛使用的浮点数表示标准。对于一个具体的数值,如10.2345434,它会被分解为符号位、指数位和尾数位。这里以最常见的float32(单精度浮点数)为例来解释这个过程:符号位如果数值是正的,符号位为0;如果数值是负的,符号位为1。对于10.23454......
  • LLM场景下常用浮点数介绍
    在计算机中,浮点数的表示基于IEEE754标准,这是最广泛使用的浮点数表示标准。对于一个具体的数值,如10.2345434,它会被分解为符号位、指数位和尾数位。这里以最常见的float32(单精度浮点数)为例来解释这个过程:符号位如果数值是正的,符号位为0;如果数值是负的,符号位为1。对于10.23454......
  • LLM场景下常用浮点数介绍
    在计算机中,浮点数的表示基于IEEE754标准,这是最广泛使用的浮点数表示标准。对于一个具体的数值,如10.2345434,它会被分解为符号位、指数位和尾数位。这里以最常见的float32(单精度浮点数)为例来解释这个过程:符号位如果数值是正的,符号位为0;如果数值是负的,符号位为1。对于10.23454......
  • LLM场景下常用浮点数介绍
    在计算机中,浮点数的表示基于IEEE754标准,这是最广泛使用的浮点数表示标准。对于一个具体的数值,如10.2345434,它会被分解为符号位、指数位和尾数位。这里以最常见的float32(单精度浮点数)为例来解释这个过程:符号位如果数值是正的,符号位为0;如果数值是负的,符号位为1。对于10.23454......
  • LLM场景下常用浮点数介绍
    在计算机中,浮点数的表示基于IEEE754标准,这是最广泛使用的浮点数表示标准。对于一个具体的数值,如10.2345434,它会被分解为符号位、指数位和尾数位。这里以最常见的float32(单精度浮点数)为例来解释这个过程:符号位如果数值是正的,符号位为0;如果数值是负的,符号位为1。对于10.23454......
  • 5、主板品牌分类介绍:微星 - 计算机硬件品牌系列文章
    微星科技(‌MSI)‌是一家全球知名的IT配件公司,‌专注于主板、‌显卡等产品的研发、‌生产和销售。‌微星科技成立于1986年8月,‌总部位于台湾新北市中和区。‌由徐祥、‌黄金请、‌林文通、‌游贤能、‌卢琪隆五位曾任职于台湾新力公司的工程师共同创办。‌自创立以来,‌微星......
  • (11-1)基于SLAM的自主路径导航系统:背景介绍+项目介绍
    在本章的内容中,通过具体实例展示了实现一个自主路径导航系统的过程。本项目利用TurtleBot3机器人和ROS框架实现了自主路径规划功能,通过SLAM技术进行实时地图建立和定位,并结合move_base包实现路径规划。用户可以根据需求选择不同的SLAM方法,包括gmapping、cartographer、hector......
  • 文件系统 FTP Ubuntu 安装入门介绍
    文件服务系列文件存储服务系统(FileStorageServiceSystem)-00-文件服务器是什么?为什么需要?文件存储服务系统(FileStorageServiceSystem)-01-常见的文件协议介绍文件系统FTPUbuntu安装入门介绍文件存储服务系统(FileStorageServiceSystem)-02-SFTP协议介绍分布式文件服......
  • Shell编程基本介绍
    文章目录Shell简介Shell环境第一个Shell脚本Shell脚本的运行方法基础语法Shell变量定义变量使用变量修改变量的值单引号和双引号的区别将命令的结果赋值给变量只读变量删除变量Shell传递参数特殊字符处理参数说明$*与$@区别Shell字符串三种形式的区别获取字符串长......