您现在的位置是:首页 >技术杂谈 >UnitTest 学习网站首页技术杂谈

UnitTest 学习

九儿九知 2024-10-08 00:01:03
简介UnitTest 学习

一、UnitTest 基本使用

  1. UnItTest 框架介绍
    UnitTest是python自带的一个单元测试框架,测试人员也经常用来做自动化测试,用来管理和执行用例
  2. 使用的原因:
    能够组织多个用例去执行
    提供丰富的断言方法
    能够生成测试报告
  3. 核心要素
1. TestCase 测试用例,这个测试用例是unittest的组成部分,作用是用来书写真正的用例代码(脚本)
2. Testsuite 测试套件,作用是用来组装(打包)Testcase的,即可以将多个用例脚本文件组装到一起
3. TestRunner 测试执行,作用是用例执行 Testsuite(测试套件)的
4. TestLoader 测试加载,是对Testsuite (测试套件)功能的补充,作用是用来组装(打包)Testcase的
5. Fixture 测试夹具,是一种代码结构,书写前置方法(执行用例之前的方法)代码和后置方法(执行用例之后的方法)代码

1. TestCase 测试用例

书写真正的用例代码(脚本)

  • 步骤
    • 导包 unnitest:import unittest
    • 定义测试类,需要继承unittest.TestCase类,习惯性类名以Test开头
    • 书写测试方法,必须以test开头
    • 执行
  • 注意事项
    代码的文件名字,需要满足标识符的规则
'''
TestCase 测试用例的使用
'''
import unittest


# 继承unittest.TestCase类,就是测试类
class TestDemo(unittest.TestCase):
    # 书写测试方法,方法名必须以test开头
    def test_method1(self):
        print("测试方法1")

    def test_method2(self):
        print("测试方法2")

# 执行 在类名或者方法名后右键运行
# 在主程序使用 unittest.main()来执行
if __name__ == '__main__':
    unittest.main()

2. TestSuite 和 TestRunner

  1. TestSuite (测试套件)
    将多条用例脚本集合在一起,就是套件,即用来组装用例的
  • 步骤
    – 导包 unittest
    – 实例化套件对象 unittest.TestSuite()
    – 添加用例方法
  1. TestRunner (测试执行)
  • 步骤
    – 导包 unittest
    – 实例化执行对象 unittest.TextTestRunner()
    – 执行对象执行套件对象 执行对象.run(套件对象)
import unittest
# 实例化套件对象
from day0610.testcase import TestDemo
# 实例化套件对象
suite = unittest.TestSuite()

# 添加用例方法
# 套件对象.addTest(测试类名('测试方法名'))
suite.addTest(TestDemo('test_method1'))
suite.addTest(TestDemo('test_method2'))

# 添加整个测试类
# 套件对象.addTest(unittest.makeSuite('测试类名'))
suite.addTest(unittest.makeSuite(TestDemo))

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

3. TestLoader 测试加载

作用和TestSuite一样,组装用例代码,同样也需要TextTestRunner()去执行

import unittest
# 实例化加载对象并加载用例,得到套件对象
# suite = unittest.TestLoader().discover('用例所在的目录','用例代码文件名.py')
suite = unittest.TestLoader().discover('.','testcase.py')
# 实例化执行对象并执行
# runner = unittest.TextTestRunner()
# runner.run(suite)
unittest.TextTestRunner().run(suite)
  • TestLoader 和 TestSuite的区别
    • TestSuite
      灵活,方便控制要执行的测试用例,但是需要手动添加测试用例
    • TestLoader
      可以自动搜索满足条件的测试用例,不方便控制要执行的测试用例

4. Fixture

在用例执行前后会自动执行的代码结构

  • 方法级别 Fixture
    在每个用例执行前后都会自动调用,方法名是固定的
 # 每个用例执行前都会自动调用
    def setUp(self): # 前置
        pass

    # 每个用例执行后都会自动调用
    def tearDown(self): # 后置
        pass
  • 类级别 Fixture
    在类中所有的测试方法执行前后,会自动执行的代码,只执行一次
@classmethod
def setUpClass(cls): # 类前置
    pass

@classmethod
def tearDownClass(cls): # 类后置
    pass
  • 模块级别 Fixture(了解)
    在代码文件执行前后执行一次
def setUpModule():
    pass
    
def tearDownModule():
    pass

二、断言与参数化

跳过:某些用例由于某种原因不想执行,设置为跳过
生成测试报告:suite和runner(第三方)

断言

断言:使用代码自动地判断预期结果和实际结果是否相符

  • assertEqual(预期结果,实际结果)
    判断预期结果和实际结果是否相等,如果相等,用例通过;如果不相等,抛出异常,用例不通告
  • assertIn(预期结果,实际结果)
    判断预期结果是否包含在实际结果中,如果存在,用例通过;如果不存在,抛出异常,用例不通告
import unittest
class TestAssert(unittest.TestCase):
    def test_equal_1(self): 
        self.assertEqual(10, 10) # 用例通过

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

    def test_in(self):
        self.assertIn('admin', '欢迎admin登录')  # 用例通过
  • 使用suite来运行
import unittest
from day0610.demo4 import TestAssert

suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestAssert))
unittest.TextTestRunner().run(suite)

参数化

参数化:将测试数据定义到json文件,使用

  • 通过参数的方式来传递数据,从而实现数据和脚本分离,并且可以实现用例的重复执行(在书写用例方法时,测试数据使用变量代替,在执行的时候进行数据传递)
  • unittest测试框架,本身不支持参数化,但是可以通过安装unittest扩展插件parameterized来实现
  • 环境准备
    因为参数化的插件,不是unittst自带的,所以想要使用需要进行安装,python中的包(插件、模块)的安装,使用 pip 工具
    pip install parameterized # 在cmd终端中执行
    tips: pip install -i https://pypi.douban.com/simple/ parameterized 可以临时指定pip下载源
  • 使用
    1.导包 from para… import para…
    2.修改测试方法,将测试方法中的测试数据使用变量表示
    3.组织测试数据,格式[(),(),()] 一个元组就是一组测试数据
    4.参数化,在测试方法上方使用装饰器 @parameterized.expand(测试数据)
    5.运行(直接 TestCase 或者使用 suite 运行)

  • add_data.json 要使用的数据
[
  [1,1,2],
  [1,2,3],
  [4,1,5],
  [2,3,5],
  [4,5,9]
]
  • 读取json文件,并返回需要的数据
import json

def build_add_data():
    with open('add_data.json') as f:
        data = json.load(f)  # [[],[],[]] ---> [(),(),()]
    return data
  • 使用数据
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))
  • 复杂的数据格式处理
[
  {
    "a": 1,
    "b": 2,
    "expect": 3
  },
...
]
def build_add_data_1():
    with open('add_data_1.json') as f:
        data_list = json.load(f)  # [{},{},{}}] ---> [(),(),()]
        new_list = []
        for data in data_list:
            # 简化写法,因为字典里所有值都是我们需要的,如果有某些值不需要,再使用get获取所需的键值
            new_list.append(tuple(data.values()))
            # a = data.get('a')
            # b = data.get('b')
            # expect = data.get('expect')
            # new_list.append((a,b,expect))
        return new_list

三、测试报告

HTML测试报告:执行完测试用例之后,以HTML方式将执行结果生成报告

  • 安装
    pip install HTMLTestReport
  • 使用
    1.导包 unittest、HTMLTestReport
    2.组装用例(套件、loader)
    3.使用HTMLTestReport中的runner执行套件
    4.查看报告
import unittest
from htmltestreport import HTMLTestReport

# 套件
from day0610.demo6 import TestAdd

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

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

在这里插入图片描述

获取项目的绝对路径

  • 项目是分目录书写的,使用相对路径,可能会出现找不到文件的情况,此时需要使用相对路径
  • 方法:
    1.在项目的根目录中,创建一个python文件(app.py)
    2.在这个文件中,获取项目的目录,在其他代码中使用路径拼接完成绝对路径的书写
    获取当前文件的绝对路径:abspath = os.path.abspath(_file_)
    获取文件路径的目录名称:dirname = os.path.dirname(filepath)
import os

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

path2 = os.path.dirname(path1) # D:PyCharmCodestudyTypeday0610
print(path2)

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

登录案例

  1. 对登录函数进行测试,登录函数定义在tool.py中
  2. 在case中书写测试用例对login函数 进行测试,使用断言
  3. 将login函数的测试数据定义在json文件中,完成参数化(data目录)
  4. 生成测试报告,在report目录中
  • 登录函数
def login(username,password):
    if username == 'admin' and password == '123456':
        return '登录成功'
    else:
        return '登录失败'
  • 测试数据
# login_data.json
[
  {
    "desc": "正确的用户名和密码",
    "username": "admin",
    "password": "123456",
    "expect": "登录成功"
  },
  {
    "desc": "错误的用户名",
    "username": "root",
    "password": "123456",
    "expect": "登录失败"
  },
  {
    "desc": "错误的密码",
    "username": "admin",
    "password": "123",
    "expect": "登录失败"
  },
  {
    "desc": "错误的用户名和密码",
    "username": "root",
    "password": "123",
    "expect": "登录失败"
  }
]
  • 读取数据的方法
# read_data.py
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
  • 测试用例代码
# test_login.py
import unittest

from day0610.commom.read_data import build_login_data
from day0610.tool 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代码
# login.py
import unittest

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

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

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

跳过

对于一些未完成的或者不满足测试条件的测试函数和测试类,可以跳过执行

  • 使用方法
    直接将函数标记成跳过:@unittest.skip(‘跳过的原因’)
    根据条件判断测试函数是否跳过:@unittest.skipif(condition,reason)
import unittest

version = 10


class TestSkip(unittest.TestCase):
    @unittest.skip('不想执行')
    def test_1(self):
        print('方法1')

    @unittest.skipIf(version >= 30, '版本号大于等于30,方法不执行')
    def test_2(self):
        print('方法2')

    def test_3(self):
        print('方法3')


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

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。