unittest框架核心要素及应用

1. unittest核心要素

  • unittest介绍
    • 测试框架,不仅仅用于单元测试
    • python自动的测试包
    • 用法和django.test.TestCase类似

1.1【知道】unittest介绍和核心要素

1.2【掌握】unittest核心要素的使用

"""
1. 测试用例
2. 容器,容器添加测试用例
3. 运行容器中的测试用例
"""
import unittest
​
# 测试类,继承于unittest.TestCase
class MyTest(unittest.TestCase):
    def test_1(self):
        print('test_1')
​
    def test_2(self):
        print('test_2')
​
if __name__ == '__main__':
    # 类的外面
    # 2. 容器,容器添加测试用例
    suite = unittest.TestSuite()
    # 测试用例的执行顺序,按添加的顺序执行
    suite.addTest(MyTest('test_1'))
    suite.addTest(MyTest('test_2'))
​
    # 3. 运行容器中的测试用例
    runner = unittest.TextTestRunner()
    runner.run(suite)

注意:只能通过命令运行

python xxx.py

1.3【知道】Fixture

  • 测试类中:实现了前、后置方法,它就是一个fixture

1.4【掌握】defaultTestLoader

"""
1. 测试用例
2. 容器,容器添加测试用例
3. 运行容器中的测试用例
"""
import unittest
​
# 测试类,继承于unittest.TestCase
class MyTest(unittest.TestCase):
    def test_1(self):
        print('test_11111111111111')
​
    def test_2(self):
        print('test_22222222222222')
​
if __name__ == '__main__':
    # 2. 容器,容器添加测试用例
    # 默认找指定路径下,所有test开头的文件
    # 参数1:路径,参数2:指定的文件
    suite = unittest.defaultTestLoader.discover('./', 'test_2_demo.py')
​
    # 3. 运行容器中的测试用例
    runner = unittest.TextTestRunner()
    runner.run(suite)

注意:只能通过命令运行

python xxx.py

2.【掌握】unittest基本使用

"""
1. 导入unittest模块
2. 新建类,继承于unittest.TestCase
3. 类中方法
    1. 前、后置(不是必须的,有前置必须写后置,匹配的)
    2. test开头的测试用例(测试用例中有断言)
4. unittest.main()运行测试
"""
import unittest
​
class MyTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        print('setUpClass')
​
    @classmethod
    def tearDownClass(cls) -> None:
        print('tearDownClass')
​
    def setUp(self) -> None:
        print('setUp')
​
    def tearDown(self) -> None:
        print('tearDown')
​
    def test_1(self):
        print('test_1111111')
​
    def test_s(self):
        print('test_s')
        a = 1 + 1
        self.assertEqual(a, 2, '结果不为2')
​
    def test_f(self):
        print('test_f')
        a = 1 + 2
        self.assertEqual(a, 3, '结果不为3')
​
    def xxx_xxx(self): # 不会执行非test开头的方法
        print('xxxxx')
​
if __name__ == '__main__':
    unittest.main()
  • 测试用例运行顺序:0~9, A~Z, a~z
  • python代码运行,pycharm直接右击运行,等价于:python xxx.py

3.【知道】断言

# arg1和arg2相等,测试通过,标志为 .
# arg1和arg2不相等,测试不通过,标志为 F,抛出异常,显示msg的信息
unittest.assertEqual(arg1, arg2, msg=None)

4.【掌握】参数化

import unittest
from parameterized import parameterized # 需要先安装模块
​
class MyTest(unittest.TestCase):
    def setUp(self) -> None:
        print('setUp')
​
    def tearDown(self) -> None:
        print('tearDown')
    
    # 1. 参数化参数:列表套元组
    # 2. 列表有几个元素,测试用来执行几次
    # 3. 元组元素的位置匹配测试用例的形参
    @parameterized.expand([('mike', '123'), ('yoyo', 'abc')])
    def test_params(self, name, pwd):
        print('name = %s, pwd = %s'%(name, pwd))
​
​
if __name__ == '__main__':
    unittest.main()

5. mock

5.1 mock介绍

5.2 Mock 类基本使用

5.2.1【掌握】return_value

import unittest
import unittest.mock
​
class MyTest(unittest.TestCase):
    def test_return(self):
        # 1. 创建Mock()对象,给return_value关键字传参
        mock_obj = unittest.mock.Mock(return_value=250)
        # mock_obj是对象,可调用对象,用法和函数一样
        ret = mock_obj()
        print(ret)

5.2.2【知道】side_effect

import unittest
import unittest.mock
​
class MyTest(unittest.TestCase):
    def test_except(self):
        # 1. 创建Mock()对象,传递异常对象
        mock_obj = unittest.mock.Mock(side_effect=BaseException('自定义异常'))
        # mock_obj是对象,可调用对象,用法和函数一样
        mock_obj()
​
    def test_list(self):
        # 1. 创建Mock()对象,传递list
        mock_obj = unittest.mock.Mock(side_effect=[1,2,3])
        # mock_obj是对象,可调用对象,用法和函数一样
        print(mock_obj())
        print(mock_obj())
        print(mock_obj())
        print(mock_obj()) # err, StopIteration
​
    def test_func(self):
        def func(a, b):
            return a+b
​
        # 1. 创建Mock()对象,传递函数名
        mock_obj = unittest.mock.Mock(side_effect=func)
        # mock_obj是对象,可调用对象,用法和函数一样
        print(mock_obj(1, 1))
        # TypeError: func() missing 2 required positional arguments: 'a' and 'b'
        mock_obj()

5.2.3【掌握】mock案例

5.3 限制模拟的范围

class MyTest(unittest.TestCase):
    def test_1(self):
        # unittest.mock.Mock
        # 通过这种方式Mock后,后面调用的测试用来也有效果(保证mock先执行)
        pay.pay_way = unittest.mock.Mock(return_value={"result": "success", "reason":"null"})
​
        ret = pay_status.pay_way_status()
        print("test_1 = ", ret)
        self.assertEqual(ret, '支付成功', '支付失败')
​
    def test_2(self):
        ret = pay_status.pay_way_status()
        print("test_2 = ", ret)
        self.assertEqual(ret, '支付成功', '支付失败')

5.3.1【掌握】mock.path

5.3.2【掌握】patch 上下文管理器

  • 通过mock.path或者patch 上下文管理器,限制mock范围
  • patch 上下文管理器注意作用域问题
import unittest
import unittest.mock
import pay
import pay_status
​
​
class MyTest(unittest.TestCase):
    @unittest.mock.patch('pay.pay_way')
    def test_1(self, mock_obj):
        mock_obj.return_value = {"result": "success", "reason":"null"}
​
        ret = pay_status.pay_way_status()
        print("test_1 = ", ret)
        self.assertEqual(ret, '支付成功', '支付失败')
​
    def test_3(self):
        with unittest.mock.patch('pay.pay_way') as mock_obj:
            mock_obj.return_value = {"result": "success", "reason":"null"}
​
            ret = pay_status.pay_way_status()
            print("test_3 = ", ret)
            self.assertEqual(ret, '支付成功', '支付失败')
​
​
    def test_2(self):
        ret = pay_status.pay_way_status()
        print("test_2 = ", ret)
        self.assertEqual(ret, '支付成功', '支付失败')
​
if __name__ == '__main__':
    unittest.main()

5.4【掌握】类方法替换

from unittest import mock
import unittest
​
class Pay(object):
    def pay_way(self):
        """假设这里是一个支付的功能,未开发完
        支付成功返回:{"result": "success", "reason":"null"}
        支付失败返回:{"result": "fail", "reason":"余额不足"}
        reason返回失败原因
        """
        raise NotImplementedError('代码还没有实现')
​
    def pay_way_status(self):
        """根据支付的结果success或fail,判断跳转到对应页面
        假设这里的功能已经开发完成"""
​
        # todo 此时pay_way()函数并未完成!你先假定他完成了
        result = self.pay_way()
        print(result)
​
        if result["result"] == "success":
            return "支付成功"
        if result["result"] == "fail":
            return "支付失败"
​
class TestPayStatues(unittest.TestCase):
    '''单元测试用例'''
    def test_1(self):
        p = Pay()
        p.pay_way = unittest.mock.Mock(return_value={"result": "success", "reason":"null"})
​
        ret = p.pay_way_status()
        self.assertEqual(ret, '支付成功', '测试失败')
​
    @unittest.mock.patch.object(Pay, 'pay_way')
    def test_2(self, mock_obj):
        mock_obj.return_value={"result": "success", "reason":"null"}
​
        p = Pay()
        ret = p.pay_way_status()
        self.assertEqual(ret, '支付成功', '测试失败')
​
    def test_3(self):
        with unittest.mock.patch.object(Pay, 'pay_way') as mock_obj:
            mock_obj.return_value={"result": "success", "reason":"null"}
    
            p = Pay()
            ret = p.pay_way_status()
            self.assertEqual(ret, '支付成功', '测试失败')

5.5 常用的方法和属性

import unittest
import unittest.mock
​
​
class MockTest(unittest.TestCase):
    def test_return_value(self):
        mock_obj = unittest.mock.Mock(return_value=1999)
        result = mock_obj()
        print(result)  # 打印 1999
​
        mock_obj()
        print(mock_obj.called)  # 是否被调用过, 返回布尔值
        print(mock_obj.call_count)  # 获取调用测试, 返回调用测试

6. 测试报告

6.1【知道】HTMLTestRunner

"""
0. 导包, import unittest
1. 定义类,继承unittest.TestCase
2. 是一个fixture, 有前置后置方法
3. 有test开头的测试用例,结果用断言判断
4. 运行测试
"""
import unittest
from HTMLTestRunner.HTMLTestRunner import HTMLTestRunner # 需要安装插件
​
​
class MyTest(unittest.TestCase):
    def setUp(self) -> None:
        print('setUp')
​
    def tearDown(self) -> None:
        print("tearDown")
​
    @classmethod
    def setUpClass(cls) -> None:
        print('setUpClass')
​
    @classmethod
    def tearDownClass(cls) -> None:
        print('tearDownClass')
​
    def test_1_add(self):
        num = 1 + 2
        print('test_add')
        self.assertEqual(num, 3, msg='加法错误')
​
    def test_2_sub(self):
        num = 1 - 1
        print('test_sub')
        self.assertEqual(num, 3, msg='减法错误')
​
​
if __name__ == '__main__':
    # 1. 把测试用例添加到suite容器中
    suite = unittest.defaultTestLoader.discover('./', 'test_1.py')
​
    # 2. 打开文件,是一个文件对象
    with open('./HTMLTestRunner.html', 'w', encoding='utf-8') as f:
        # 3. HTMLTestRunner()创建一个runner对象
        runner = HTMLTestRunner(
            stream=f,  # 测试报告需要写入到的文件
            verbosity=2,  # 控制台输出信息的详细程度, 默认为1
            title='这是报告标题',  # 测试报告的标题
            description='这是一个测试报告内容'  # 测试报告的描述
        )
        # 4. runner把容器中测试用例运行
        runner.run(suite)

6.2【知道】BeautifulReport

import unittest
from BeautifulReport import BeautifulReport
​
​
class MyTest(unittest.TestCase):
    def setUp(self) -> None:
        print('setUp')
​
    def tearDown(self) -> None:
        print("tearDown")
​
    @classmethod
    def setUpClass(cls) -> None:
        print('setUpClass')
​
    @classmethod
    def tearDownClass(cls) -> None:
        print('tearDownClass')
​
    def test_1_add(self):
        """add"""
        num = 1 + 2
        print('test_add')
        self.assertEqual(num, 3, msg='加法错误')
​
    def test_2_sub(self):
        """sub"""
        num = 1 - 1
        print('test_sub')
        self.assertEqual(num, 3, msg='减法错误')
​
​
if __name__ == '__main__':
    # 1. 把测试用例添加到suite容器中
    # suite = unittest.defaultTestLoader.discover('./', 'test_2.py')
    suite = unittest.defaultTestLoader.discover('./')
​
    # 2. 创建runner对象,同时把suite传参进入
    runner = BeautifulReport(suite)
​
    # 3. 运行,同时生成测试报告
    # 参数1:生成文件的注释, 参数2:生成文件的filename, 参数3:生成report的文件存储路径
    runner.report('报告描述必须有,在报告中显示为用例名称', '测试报告文件名2', './')

7. 综合案例

7.1【知道】测试流程和目录结构

7.2【掌握】基础工具方法类:读取json数据

# 读取json文件
# 1. 传入文件,读取内容,返回文件中数据
# 路径的处理
import os
import json
​
​
class Data(object):
    # 类属性
    # 获取工程所在的绝对路径
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
​
    # 设置为类方法,是为了方便调用
    @classmethod
    def read_info(cls, file_name='server.json'):
        """
        :param file_name: 文件名
        :return: 文件中的json数据
        """
        file_path = os.path.join(cls.BASE_DIR, 'data', file_name)
        # print(file_path)
​
        # 只读方式打开文件
        with open(file_path, 'r', encoding='utf-8') as f:
            ret = json.load(f) # load, 加载,加载是读
            # print(ret)
​
        return ret
​
# 测试一下
# print(Data.BASE_DIR)
# ret = Data.read_info('server.json')
# ret = Data.read_info('users/login_users.json')
# print('ret = ', ret)

7.3【掌握】基础工具方法类:请求封装

"""
1. 发送请求,只需要指定路径‘/login/’,无需指定url, 其他参数和原来的一样
import requests
​
def xxx(path = '/info/', params=None, **kwargs):
    url = 'http://127.0.0.1:8000'
    requests.get(url + path, params, **kwargs)
"""
from utils.data import Data
import requests
​
​
def read_url():
    ret = Data.read_info('server.json')
    # {'schema': 'http', 'host': '127.0.0.1', 'port': 8000}
    # print(ret)
    url = f'{ret["schema"]}://{ret["host"]}:{ret["port"]}'
​
    return url
​
​
# print(read_url())
class Ask(object):
    # 类熟悉
    URL = read_url()
​
    @classmethod
    def get(cls, path, params=None, **kwargs):
        """
        :param path: 路径, '/info/'
        :param params: url参数
        :param kwargs: 其他参数
        :return: requests.Response 响应对象
        """
        resp = requests.get(cls.URL + path, params, **kwargs)
        return resp
​
    @classmethod
    def post(cls, path, data=None, json=None, **kwargs):
        """
        :param path: 路径, 如'/login/'
        :param data: 表单格式数据
        :param json: json格式数据
        :param kwargs: 其他参数
        :return: requests.Response 响应对象
        """
        resp = requests.post(cls.URL + path, data, json, **kwargs)
        return resp
​
​
# 创建对象,调用方法
# obj = Ask()
# ret = obj.get('/info/')
# print(ret.json())
​
# info = {
#     "username": "admin111",
#     "password": "chuanzhi12345",
#     "remembered": True
# }
# ret = Ask.post('/login/', json=info)
# print(ret.json())

7.4【掌握】测试用例编写

import unittest
from utils.ask import Ask
​
​
class MyTest(unittest.TestCase):
    def test_login(self):
        print('test_login')
        info = {
            "username": "admin",
            "password": "chuanzhi12345",
            "remembered": True
        }
        r = Ask.post('/login/', json=info)
        ret = r.json()
        print(ret)
​
        self.assertEqual(ret['code'], 0, msg=ret['errmsg'])
​
​

7.5【掌握】执行测试用例

import unittest
from BeautifulReport import BeautifulReport
​
if __name__ == '__main__':
    # 1. 把测试用例添加到suite容器中
    suite = unittest.defaultTestLoader.discover('./tests')
​
    # 2. 创建runner对象,同时把suite传参进入
    runner = BeautifulReport(suite)
​
    # 3. 运行,同时生成测试报告
    # 参数1:生成文件的注释, 参数2:生成文件的filename, 参数3:生成report的文件存储路径
    runner.report('登陆', '美多登陆测试报告', './report')

推荐学习:

Python入门教程完整版(懂中文就能学会)

完整视频:Python入门教程完整版(懂中文就能学会)

  • 5
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值