Python进阶(1) | 使用VScode写单元测试

Python进阶(1) | 单元测试

2024.01.28
VSCode: 1.85.1
Linux(ubuntu 22.04)

文章目录

  • Python进阶(1) | 单元测试
    • 1. 目的
    • 2. Python Profile
    • 3. 单元测试框架
      • 3.1 什么是单元测试
      • 3.2 选一个单元测试框架
      • 3.3 编写 Python 单元测试代码
      • 3.4 在 VSCode 里发现单元测试
      • 3.5 再写一个单元和测试: IoU 的计算
    • 4. 总结
    • 5. References

1. 目的

使用 Python 实现一些小工具、库的时候,增加单元测试来保证正确性。

重读 VSCode 的 Python 官方文档, 更新个人的 Python 开发效率。

2. Python Profile

VSCode 提供了定制 profile 的功能, 个人目前理解为类似于 vim/emacs 里的模式的升级版。以前我只是配置VSCode的全局配置和当前工程配置, 而 Profile 则是建立了不同的配置,每个打开的VSCode工程都可以在不同的 profile 之间切换。

举例: 分别设置 C++ Profile 和 Python profile, 在 Python profile 和 C++ profile 中使用不同的快捷键、不同的UI布局等。

关于 profile 的完整文档在 https://code.visualstudio.com/docs/editor/profiles

官方提供了 Python 的profile,可以根据这个预定义的 profile, 继承它,创建一个自己的 Python profile:
https://code.visualstudio.com/docs/editor/profiles#_python-profile-template

3. 单元测试框架

3.1 什么是单元测试

A unit is a specific piece of code to be tested, such as a function or a class. Unit tests are then other pieces of code that specifically exercise the code unit with a full range of different inputs, including boundary and edge cases. Both the unittest and pytest frameworks can be used to write unit tests.

所谓单元,指的是一段特定的要被测试的代码,比如说一个函数、一个类。
所谓测试,指的是被测试代码A之外的代码B, 也就是说B这部分代码存在的意义,就是测试A这部分代码。
测试代码通常需要包含各种不同的输入,包括边界情况。
单元测试仅仅关注输入 和 输出, 不关注代码实现的细节。

因此,所谓单元测试,首先需要划分出单元,然后针对每个单元(或者仅对于关注的单元),编写测试代码。

For each input, you then define the function’s expected return value (or values).

对于被测试的代码的每一种输入,你需要定义它的预期结果。

With all the arguments and expected return values in hand, you now write the tests themselves, which are pieces of code that call the function with a particular input, then compare the actual return value with the expected return value (this comparison is called an assertion):

然后调用被测试的代码A: 给它传入输入, 获得它的输出结果, 并且和你预设的结果进行比对,结果一样则成功,不一样则报告失败。

https://code.visualstudio.com/docs/python/testing

3.2 选一个单元测试框架

Python 最常用的单元测试框架: unittest 和 pytest.

unittest 是 Python 标准库的模块, 也就是 Python 安装后自带的。 pytest 则需要自行安装: pip install pytest.

3.3 编写 Python 单元测试代码

首先,是被测试的单元的代码, inc_dec.py:

def increment(x: int):return x + 1def decrement(x: int):return x - 1

然后, 是编写测试代码. 先用 unittest 写一遍:test_unittest.py

import inc_dec
import unittestclass Test_TestIncrementDecrement(unittest.TestCase):def test_increment(self):self.assertEqual(inc_dec.increment(3), 4)# 这个测试用例一定会失败,是刻意做的def test_decrement(self):self.assertEqual(inc_dec.decrement(3), 4)if __name__ == '__main__':unittest.main()

再用 pytest 写一遍, 写法更简单:

import inc_decdef test_increment():assert inc_dec.increment(3) == 4# 这个测试用例一定会失败,是刻意做的
def test_decrement():assert inc_dec.decrement(3) == 4

3.4 在 VSCode 里发现单元测试

首先在 VSCode 里点击左侧的 Testing 按钮, 创建测试相关的配置:
在这里插入图片描述

它对应到 .vscode/setting.json 里的内容:

{"python.testing.pytestArgs": ["."],"python.testing.unittestEnabled": false,"python.testing.pytestEnabled": true
}

然后点击 Testing 视图中的测试用例中最上方的按钮, 会自动发现和执行所有的测试用例:
在这里插入图片描述

在 Testing 界面中点击到 “失败” (红色) 的case, 会看到失败的具体测试代码。我们发现是测试代码本身写错, 于是改掉, 然后重新在 Testing 界面中执行测试:
在这里插入图片描述

最终,我们看到 Testing 界面中的每一项都是绿色, 表示都成功了:
在这里插入图片描述

3.5 再写一个单元和测试: IoU 的计算

前面给出的 inc_dec.py 的代码太简单, 测试代码也不太符合预期解决的问题。

单元测试的预期目的,是发现单元中的bug。这次写一个经典的计算两个Box的IoU的函数,并且故意缺少处理非法box长度的情况。

bbox.py:

# define Box class
class Box(object):def __init__(self, x, y, w, h, score):self.x = xself.y = yself.w = wself.h = hself.score = scoredef __repr__(self):return 'Box(x=%f, y=%f, w=%f, h=%f, score=%f)' % (self.x, self.y, self.w, self.h, self.score)# calculate IoU of two boxes
def box_iou(box1, box2):# get coordinates of intersecting rectanglex_left = max(box1.x, box2.x)y_top = max(box1.y, box2.y)x_right = min(box1.x + box1.w, box2.x + box2.w)y_bottom = min(box1.y + box1.h, box2.y + box2.h)# if x_right < x_left or y_bottom < y_top:#     return 0.0# intersection areaintersection_area = (x_right - x_left) * (y_bottom - y_top)# union areabox1_area = box1.w * box1.hbox2_area = box2.w * box2.hunion_area = box1_area + box2_area - intersection_areareturn intersection_area / union_area

test_bbox.py:

import bboxdef test_box_iou():box1 = bbox.Box(0, 0, 1, 1, 0.9)box2 = bbox.Box(0, 0, 1, 1, 0.9)assert bbox.box_iou(box1, box2) == 1.0def test_box_iou2():box1 = bbox.Box(0, 0, 1, 1, 0.9)box2 = bbox.Box(1, 1, 2, 2, 0.9)assert bbox.box_iou(box1, box2) == 0def test_box_iou3(): # 这个例子是边界case,很容易失败box1 = bbox.Box(0, 0, 0, 0, 0.9)box2 = bbox.Box(1, 1, 1, 1, 0.9)assert bbox.box_iou(box1, box2) == 0

上述代码在 test_box_iou3() 时失败了, 错误类型是出现了除0错误。显然,除非两个 box 大小都是0,否则不会出现除以0的情况。于是很偷懒的改了一下:

# calculate IoU of two boxes
def box_iou(box1, box2):# get coordinates of intersecting rectanglex_left = max(box1.x, box2.x)y_top = max(box1.y, box2.y)x_right = min(box1.x + box1.w, box2.x + box2.w)y_bottom = min(box1.y + box1.h, box2.y + box2.h)# if x_right < x_left or y_bottom < y_top:#     return 0.0# intersection areaintersection_area = (x_right - x_left) * (y_bottom - y_top)# union areabox1_area = box1.w * box1.hbox2_area = box2.w * box2.hunion_area = box1_area + box2_area - intersection_areaif union_area == 0:return 0return intersection_area / union_area

再增加一个侧测试用例:当box本身的宽度或高度为负值时,预期结果我们设置为0. 测试代码是:

def test_box_iou4():box1 = bbox.Box(0, 0, -1, -1, 0.9)box2 = bbox.Box(0, 0, 2, 2, 0.9)iou = bbox.box_iou(box1, box2)assert iou == 0

IoU的实现代码,仍然是用很偷懒的修改:

# calculate IoU of two boxes
def box_iou(box1, box2):# get coordinates of intersecting rectanglex_left = max(box1.x, box2.x)y_top = max(box1.y, box2.y)x_right = min(box1.x + box1.w, box2.x + box2.w)y_bottom = min(box1.y + box1.h, box2.y + box2.h)# if x_right < x_left or y_bottom < y_top:#     return 0.0# intersection areaintersection_area = (x_right - x_left) * (y_bottom - y_top)# union areabox1_area = box1.w * box1.hbox2_area = box2.w * box2.hunion_area = box1_area + box2_area - intersection_area# if union_area == 0:#     return 0# if box1.w < 0 or box1.h < 0 or box2.w < 0 or box2.h < 0:#     return 0return intersection_area / union_area

此时的测试仍然不够完备。再补充一个:

def test_box_iou5():box1 = bbox.Box(0, 0, 0, 0, 0.9)box2 = bbox.Box(0, 0, 0, 0, 0.9)iou = bbox.box_iou(box1, box2)assert iou == 0

现在,把包含了补丁的 box_iou() 重构一番,得到:

# calculate IoU of two boxes
def box_iou(box1, box2):# get coordinates of intersecting rectanglex_left = max(box1.x, box2.x)y_top = max(box1.y, box2.y)x_right = min(box1.x + box1.w, box2.x + box2.w)y_bottom = min(box1.y + box1.h, box2.y + box2.h)if x_right <= x_left or y_bottom <= y_top:return 0.0# intersection areaintersection_area = (x_right - x_left) * (y_bottom - y_top)# union areabox1_area = box1.w * box1.hbox2_area = box2.w * box2.hunion_area = box1_area + box2_area - intersection_areareturn intersection_area / union_area

4. 总结

VSCode 的 Testing 视图,改善了运行单元测试的交互界面。传统的 C/C++ 中, gtest 框架通过传入 --gtest_filter=xxx 来过滤测试, 在 VSCode 面前仍然落后。

至于单元测试代码是否够好, 一个标准是覆盖率的高低, 就像 IoU 的例子, 第一次用 ChatGPT 生成代码时,虽然看似正确, 但其实 test_box_iou5() 这个测试用例(两个box的大小都是0,并且重合)是无法通过的。

因此, VSCode 的 Testing 界面仅仅是锦上添花, 单元测试的编写仍然需要考虑周全。

5. References

  • https://code.visualstudio.com/docs/editor/profiles#_python-profile-template
  • https://code.visualstudio.com/docs/python/testing

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/653712.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】补充和navicat的一些简单使用

文章目录 前言在这里插入图片描述 事情起因因为这个articlecount的c是小写了&#xff0c;我想改成大写 一、修改二、navicat的使用步骤1.连接2.建库&#xff0c;建表 三.填写数据总结 前言 事情起因因为这个articlecount的c是小写了&#xff0c;我想改成大写 提示&#xff1a;…

Redis 学习笔记 2:Java 客户端

Redis 学习笔记 2&#xff1a;Java 客户端 常见的 Redis Java 客户端有三种&#xff1a; Jedis&#xff0c;优点是API 风格与 Redis 命令命名保持一致&#xff0c;容易上手&#xff0c;缺点是连接实例是线程不安全的&#xff0c;多线程场景需要用线程池来管理连接。Redisson&…

一文搞懂设计模式—策略模式

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 微信公众号&#xff1a;Java随想录 文章目录 使用场景策略模式实现策略模式的优缺点策略模式优化使用Map取消 Context 类策略枚举解决策略类膨胀SpringBoot中的策略模式 总结 在软件开发中&#xff0c;经常会…

pcl应用八叉树实例

pcl应用八叉树实例 文章目录 pcl应用八叉树实例1、基本概念2、基于八叉树的空间划分及搜索操作2.1、关键函数说明2.1.2 OctreePointCloudSearch 类2.1.2 voxelSearch 函数 3、无序点云数据集的空间变化检测 1、基本概念 八叉树结构通过循环递归的划分方法对大小为2 n ∗ 2 n ∗…

C++面试宝典第25题:阶乘末尾零的个数

题目 给定一个整数n,返回n!(n的阶乘)结果尾数中零的个数。 示例 1: 输入:3 输出:0 解释:3! = 6,尾数中没有零。 示例 2: 输入:5 输出:1 解释:5! = 120,尾数中有1个零。 解析 这道题主要考察应聘者对于数学问题的分析和理解能力,以及在多个解决方案中,寻求最优…

elementUI的el-select传递item对象或其他参数的2种方法

方法1 :value“item” 绑定对象 只要:value绑定item对象就可以 value-key"value" 必须是item里的一个属性&#xff0c;绑定值为对象类型时必填 <el-select v-model"value" placeholder"请选择" value-key"value" change"cha…

(蓝桥杯每日一题)求最长回文串

问题描述 给出一个长度为 n 的小写字符串&#xff0c;求一个最长的子串 S&#xff0c;满足SXY,X&#xff0c;Y>1&#xff0c;且X,Y 均为回文串。 输入格式 输入包括一行: 第一行是一个长度为 n 的小写字符串。 输出格式 输出包括一行&#xff1a; 一行一个整数&#xff0c;表…

Material Components for Android助你打造精美App

Material Components for Android助你打造精美App 简介 Material Components for Android (MDC-Android) 是帮助开发者执行 Material Design 的工具。由谷歌的核心工程师和用户体验设计师团队开发&#xff0c;这些组件使得开发者可以可靠地开发工作流来构建美观且功能齐全的 …

期权定价模型系列[9]SABR模型

1.简介 SABR模型是由 Hagan在 2002年提出的一种随机波动率模型&#xff0c;在抛弃了原始的BSM 模型中对于波动率为某一常数的假定&#xff0c;假设隐含波动率同样是符合几何布朗运动的&#xff0c;并且将隐含波动率设定为标的价格和合约行权价的函数&#xff0c;结合了隐含波动…

12.5内存操作流(血干JAVA系列)

12.5内存操作流 12.5内存操作流ByteArraylnputStream类的主要方法ByteArrayOutputStream 类的主要方法【例12.33】使用内存操作流完成一个大写字母转换为小写字母的程序 12.5内存操作流 在 流 的 操 作 中 除 了 进 行 文 件 的 输 入 与 输 出 操 作 之 外 &#xff0c; 也 可…

flask初体验

1、定义 Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务。 中文官网 2、初步上手 1、安装flask pip3 install flask 2、创建flask应用 # -*- coding = utf-8 -*- # @Time : 2024/1/28 23:02 # @Author: Frank # @File: main.py…

说说你对vue的mixin的理解,有什么应用场景

mixin是什么 Vue中的mixin 局部混入全局混入注意事项: 使用场景源码分析Vue 的几种类型的合并策略 替换型合并型队列性叠加型小结 此文章&#xff0c;来源于印客学院的资料&#xff0c;这里只是分享&#xff0c;便于查漏补缺。 mixin是什么 Mixin 是 面向对象程序设计语言中…

回归预测 | MATLAB实现PSO-GRNN粒子群优化广义回归神经网络多输入单输出预测(含优化前后预测可视化)

回归预测 | MATLAB实现PSO-GRNN粒子群优化广义回归神经网络多输入单输出预测 目录 回归预测 | MATLAB实现PSO-GRNN粒子群优化广义回归神经网络多输入单输出预测预测效果基本介绍程序设计参考资料预测效果 <

爬虫基础-计算机网络协议

一个数据的传输 这些设备的数据转发是通过协议来完成的&#xff0c;整个互联网可以说是完全由网络协议来维持的 不同的协议分工不同&#xff0c;比如ip协议确保了ip寻址&#xff0c;tcp协议确保了数据完整性 IP地址和URL ip地址 整个网络传输可以比作快递&#xff0c;数据就…

使用毫米波雷达传感器的功能安全兼容系统设计指南1(TI文档)

摘要 功能安全标准规定了在系统中实施安全的要求&#xff0c;并有助于概括该系统要达到的安全目标。包括功能安全的系统设计不仅要降低操作不当的风险&#xff0c;还要检测故障并将其影响降到最低。随着汽车和工业系统的自主性越来越强&#xff0c;严格的功能安全要求被强制执行…

(28)Linux 信号保存 信号处理 不可重入函数

首先介绍几个新的概念&#xff1a; 信号递达(Delivery)&#xff1a;实际执行信号的处理动作。信号未决(Pending)&#xff1a;信号从产生到递达之间的状态。信号阻塞(Block)&#xff1a;被阻塞的信号产生时将保持在未决状态&#xff0c;直达解除对该信号的阻塞&#xff0c;才执…

【大厂AI课学习笔记】1.2 人工智能的应用(1)

目录 1.2 人工智能的应用 1.2.1 产业中人工智能的应用 金融 教育 医疗 交通 制造 ——智慧金融 智能风控 智能理赔 智能投研 &#xff08;声明&#xff1a;本学习笔记学习原始资料来自于腾讯&#xff0c;截图等资料&#xff0c;如有不合适摘录的&#xff0c;请与我联…

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖微信小程序端(十五)

用户端历史订单模块 1. 查询历史订单1.1 需求分析和设计1.2 代码实现1.2.1 user/OrderController1.2.2 OrderService1.2.3 OrderServiceImpl1.2.4 OrderMapper1.2.5 OrderMapper.xml1.2.6 OrderDetailMapper 2. 查询订单详情2.1 需求分析和设计2.2 代码实现2.2.1 user/OrderCon…

SpringBoot常见错误

SpringBoot常见错误 1、SpringBoot启动时报错 错误: 找不到或无法加载主类 com.xxx.xxx.Application springboot启动时报错错误&#xff1a;找不到或无法加载主类 com.xxx.xxx.Application。 解决方法就是打开idea的控制台&#xff0c;输入以下三行命令&#xff1a; mvn cl…

nginx安装ssl模块http_ssl_module

查看nginx安装的模块 /usr/local/nginx/sbin/nginx -V若出现“–with-http_ssl_module”说明已经安装过&#xff0c;否则继续执行下列步骤 进入nginx源文件目录 cd /usr/local/nginx/nginx-1.20.2重新编译nginx ./configure --with-http_ssl_module如果组件linux缺少&…