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&…

什么是JDBC?JDBC的作用是什么?

什么是JDBC&#xff1f;JDBC的作用是什么&#xff1f; JDBC是Java数据库连接&#xff08;Java Database Connectivity&#xff09;的简称&#xff0c;它是一套用于执行SQL语句的Java API。 JDBC的作用在于为Java程序提供了一个标准化的接口&#xff0c;用于访问和操作各种关系…

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

本文已收录至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…

Qt/QML编程之路:qml通过C++传递变量给另一个qml(42)

Qml的项目设计时,不可避免涉及到qml之间的调用,此时很正常想象到的是如何进行事件传输和参数传输。一般就是在qml文件中定义property int, 或者定义 signal mySignal。 我就遇到了这么一种情况,需要把一个image的source,也就是图片的路径信息,从设置界面直接传递到要设置背…

[EFI]戴尔T5810电脑 Hackintosh 黑苹果efi引导文件

硬件型号驱动情况主板 戴尔T5810&#xff0c;C610/612芯片 处理器英特尔至强E5-2620 v3已驱动内存12 GB已驱动硬盘500GB WD Blue Solid State Drive & 2TB Seagate Mobile Hard Drive (Upgraded)已驱动显卡RX 570 4Gb已驱动声卡瑞昱 Realtek ALC662 英特尔 High Definitio…

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

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

Uboot中ARMV7和ARMV8 MMU配置

问题概述 Uboot中如果打开MMU&#xff0c;则MMU需要配置MMU table来管理不同的地址空间。 其中ARMV7和ARMV8中这部分的配置代码是不同。 ARMV7的配置过程 代码参考&#xff1a;u-boot-2020.04/arch/arm/lib/cache-cp15.c mmu_setup →set_section_dcache →dram_bank_mmu_…

【GPU驱动开发】-Mesa ST和GLSL编译器衔接交互分析

前言 不必害怕未知&#xff0c;无需恐惧犯错&#xff0c;做一个Creator&#xff01; &#xff08;基于Mesa 22.2.5版本&#xff09; Mesa State Tracker 与 GLSL 编译器的协同工作是 Mesa 3D 图形渲染管线中的关键环节。这两者的衔接确保了 OpenGL API 调用能够正确、高效地…

Shell脚本转发activemg topic消息到另个activemg服务器

如果你想通过Shell脚本将ActiveMQ中的Topic消息从一个服务器转发到另一个服务器&#xff0c;你可以使用stomp命令行工具来实现。 以下是一个示例脚本&#xff0c;演示如何使用Shell脚本将ActiveMQ中的Topic消息从一个服务器转发到另一个服务器&#xff1a; #!/bin/bash# 源Act…

【论文阅读】Membership Inference Attacks Against Machine Learning Models

基于confidence vector的MIA Machine Learning as a Service简单介绍什么是Membership Inference Attacks&#xff08;MIA&#xff09;攻击实现过程DatasetShadow trainingTrain attack model Machine Learning as a Service简单介绍 机器学习即服务&#xff08;Machine Learn…

Material Components for Android助你打造精美App

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

Spring Security 的TokenStore三种实现方式

博主介绍&#xff1a;✌专注于前后端领域开发的优质创作者、秉着互联网精神开源贡献精神&#xff0c;答疑解惑、坚持优质作品共享。本人是掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战&#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 是 面向对象程序设计语言中…