单元测试总结

🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快

Hello!大家好,我是一个专注于分享软件测试干货的测试开发。

对于软件测试,我们先按照开发阶段来进行划分,将软件测试分为单元测试、集成测试、系统测试、验收测试,下面我们来聊聊单元测试。

1、什么是单元测试?

在正式阐述什么是单元测试之前,我先给大家分享一个工厂组装手机的例子。

手机组装流水线按照图纸将各个电子元件组装焊接为各个模块组件(如喇叭,听筒,麦克,FPC,按键板,摄像头,LCD等),再将各个模块组件组装成一部完整的手机。

如果一起顺利,在给手机安装系统后就可以正常使用了。但是很不幸,大多数情况下的手机是无法使用的,那么就需要将已经组装好的手机重新拆机,逐个模块排查问题,在每个模块排查中需要对每个电子元件进行检测,通过花费大量的时间和精力才能定位到问题原因。

那么在后续的生产中,如何才能避免这种问题的发生呢?

你可能立即就会想到,为什么不在组装焊接前,就先测试每个要用到的电子元器件呢?这样你就可以先排除有问题的元器件,最大程度地防止组装完成后逐级排查问题的事情发生。

实践也证明,这的确是一个行之有效的好办法。

如果把手机的生产、测试和软件的开发、测试进行类比,你可以发现:

  • 电子元器件就像是软件中的单元,通常是函数或者类,对单个元器件的测试就像是软件测试中的单元测试;
  • 组装完成的功能模块组件如喇叭,听筒,麦克,FPC,按键板,摄像头,LCD等就像是软件中的模块,对功能模块组件的测试就像是软件中的集成测试;
  • 手机全部组装并安装系统就像是软件完成了预发布版本,手机全部组装并安装系统完成后的开机测试就像是软件中的系统测试;

通过这个类比,相信你已经体会到了单元测试对于软件整体质量的重要性,那么单元测试到底是什么呢?

单元测试是指,对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作,这里的最小可测试单元通常是指函数或者类。

2、什么是好的单元测试?

好的单元测试应当包含四种特性:正确,清晰,完整,健壮

  • 正确:单元测试是最基础的要求,必须要保证所写的函数或者类实现的功能是正确的,如果实现的功能都不能满足,那就是缺陷!
  • 清晰:单元测试可以帮助其他开发理解函数或者类的实现,所以要求单元测试用例简洁、清晰,需要有良好的可读性
  • 完整:单元测试需要考虑输入与输出组合的各种场景,保证单元测试的覆盖率
  • 健壮:健壮性是最容易被忽略的一项,当被测试的类或者函数被修改内部实现或者添加功能时,一个好的单测应该完全不需要被修改或者只有极少的修改。比如⼀个排序函数的单测实现是完全稳定的,它不应该跟着不同的排序算法的变化

3、怎么写单元测试?

可能大多数的测试人员不会接触到单元测试的编写,因为按照我个人的看法,开发人员根据自己写的代码编写单测用例是最合适不过的,也是最高效的。

虽然我们不需要实际去编写单测用例,但是我们还是需要了解怎么写单元测试。

单元测试的代码结构一般包含三部分:分别是准备、调用与断言

  • 准备:准备部分的⽬的是准备好调⽤所需要的外部环境,如数据,Stub(桩代码),Mock,临时变量,调⽤请求,环境背景变量等等。
  • 调用:调⽤部分则是实际调⽤需要测试⽅法,即函数或者流程本身。
  • 断言:断⾔部分判断调⽤部分的返回结果是否符合预期。

每个单元测试都应该能清晰地分出这三部分,当然有时调⽤断⾔两部分合在⼀起也是⽐较常见的。

4、玩转单元测试

下面我们来聊聊单元测试编写用例的相关知识,首先我们需要了解单元测试的三个重要部分,即驱动程序、桩程序、Mock

驱动程序:驱动程序(Driver)也称作驱动模块,用以模拟被测模块的上级模块,能够调用被测模块。在测试过程中,驱动模块接收测试数据,调用被测模块并把相关的数据传送给被测模块。

简单说就是你负责测试的模块没有main()方法入口,所以需要写一个带main的方法来调用你的模块或方法。这个就是驱动测试

桩程序:桩程序(Stub),也称桩模块,用以模拟被测模块工作过程中所调用的下层模块,即被测模块本身调用的其他关联函数。桩模块由被测模块调用,它们一般只进行很少的数据处理。

桩是指用来代替关联代码或者未实现的代码,为了让测试对象可以正常的执行,其实一般会硬编码一些输入和输出,保证被测模块能够正常运行

Mock:Mock除了保证Stub的功能之外,还可深入的模拟对象之间的交互方式,如:调用了几次、在某种情况下是否会抛出异常以及提供数据断言

接下来我们通过一个实例来学习单元测试用例的编写

# 待测试的方法
def calculator(type):
    # 调用桩代码获取数据
    num1 = __stub1()
    num2 = __stub2()
    # 调用mock
    mock_data = __mock_check()
 
    # +
    if type.lower() == 'add':
        type = 'add'
        ret = num1+num2
        assert ret == mock_data[type]
        print('{} + {} = {}'.format(num1,num2,ret))
        return ret
    # -
    if type.lower() == 'minus':
        type = 'minus'
        ret = num1-num2
        assert ret == mock_data[type]
        print('{} - {} = {}'.format(num1,num2,ret))
        return ret
    # *
    if type.lower() == 'multiply':
        type = 'multiply'
        ret = num1*num2
        assert ret == mock_data[type]
        print('{} * {} = {}'.format(num1,num2,ret))
        return ret
    # /
    if type.lower() == 'divide':
        type = 'divide'
        if num2 == 0:
            print('除法分母不能为0')
            return '除法分母不能为0'
        else:
            ret = num1/num2
            assert ret == mock_data[type]
            print('{} / {} = {}'.format(num1,num2,ret))
            return ret
 
# 桩代码1
def __stub1():
    output = 20
    print('my stub的值是{}'.format(output))
    return output
 
# 桩代码2
def __stub2():
    output = 5
    print('my stub的值是{}'.format(output))
    return output
 
# Mock代码 => 提供断言数据
def __mock_check():
    mock_result = {}
    mock_result['add'] = 25
    mock_result['minus'] = 15
    mock_result['multiply'] = 100
    mock_result['divide'] = 4
    return mock_result
 
# 驱动程序
if __name__=="__main__":
    print(calculator('add'))
    print(calculator('minus'))
    print(calculator('multiply'))
    print(calculator('divide'))

上面提供的是一个简单的单元测试,包含了驱动程序、被测对象、桩程序以及Mock代码

  • 驱动程序__main__作为被测对象的上级模块,运行时调用被测函数calculator
  • 被测函数calculator被调用后,通过桩代码__stub1、__stub2提供测试数据
  • 被测函数calculator通过不同的入参匹配不同的场景,不同场景获取的结果与Mock函数__mock_check进行比对断言,校验结果是否符合预期

被测函数成功返回如下:

my stub的值是20
my stub的值是5
20 + 5 = 25
25

被测函数失败返回如下:

my stub的值是20
Traceback (most recent call last):
my stub的值是5
  File "/Users/Desktop/demo/unittest_demo/ut_demo.py", line 73, in <module>
    print(calculator('add'))
  File "/Users/Desktop/demo/unittest_demo/ut_demo.py", line 21, in calculator
    assert ret == mock_data[type]
AssertionError

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

immaculate C# DragDrop 注册失败 解决 C#窗口程序如何看控制台打印的日志

C# DragDrop 注册失败 System.InvalidOperationExceptionHResult0x80131509MessageDragDrop 注册失败。SourceSystem.Windows.FormsStackTrace:在 System.Windows.Forms.Control.SetAcceptDrops(Boolean accept)在 System.Windows.Forms.Control.OnHandleCreated(EventArgs e)…

怎样衡量电阻负载的好坏

电阻负载的好坏通常通过以下几种方法来衡量&#xff1a; 1. 测量电阻值&#xff1a;最直接的方法是使用万用表来测量电阻负载的电阻值。将万用表设置在适当的电阻档位&#xff0c;然后将测试笔连接到电阻负载的两个引脚上。如果电阻负载是好的&#xff0c;那么万用表应该显示一…

酒蒙子骰子小程序系统

酒蒙子流量变现小程序小游戏 后端tp8 前端uniapp 会员变现 分销推广 流量主 …

Spring Boot 3.x:自动配置类加载机制的变化

随着 Spring Boot 3.x 版本的发布&#xff0c;Spring Boot 引入了一些关键的变更。其中最重要的一项变更是 自动配置类的加载机制。在之前的版本中&#xff0c;Spring Boot 使用 spring.factories 文件来管理自动配置类的加载。然而&#xff0c;在 Spring Boot 3.x 中&#xff…

网络安全学习路线

《网络安全自学教程》 网络安全这几年改成了网络空间安全&#xff0c;因为网络空间也是国家主权之一&#xff0c;网络空间不安全&#xff0c;你就要在别人眼皮子底下裸奔&#xff0c;当然&#xff0c;非洲的小伙伴就不用担心受到威胁&#xff0c;毕竟他们连网都没有。 网络安全…

【Linux网络编程】第十一弹---HTTP协议全解析:从请求响应到方法与Header的详尽指南

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【Linux网络编程】 目录 1、HTTP 协议 1.1、认识 URL ​1.2、urlencode 和 urldecode 1.3、HTTP 协议请求与响应格式 1.3.1、代码…

从 Router 到 Navigation:HarmonyOS 路由框架的全面升级与迁移指南

在本教程中&#xff0c;我们深入探讨了 Router 和 Navigation 在 HarmonyOS 中的用法差异及如何从 Router 切换到 Navigation 的方法。重点涵盖了页面跳转、转场动画、生命周期管理以及跨包路由的实现。 页面结构对比 Router 页面结构 每个页面需要使用 Entry 注解。 页面需要…

账号下的用户列表表格分析

好的&#xff0c;这是您提供的 el-table 组件中所有列的字段信息&#xff0c;以表格形式展示&#xff1a; 列标题 (label)字段属性 (prop)对齐方式 (align)宽度 (width)是否可排序 (sortable)说明IDidcenter100否管理员的唯一标识符头像avatarcenter90否管理员的头像 URL 或路…

luckysheet与superslide冲突解决

[现象]控制台报错、界面无法操作 $是jquery。查看源码&#xff0c;发现mousewheel方法来自插件mousewheel&#xff0c;luckysheet初始应该会将mousewheel挂载在jquery上。 在控制台打印jquery取dom及其方法&#xff0c;结果如下&#xff1a; 不存在mousewheel方法&#xff0c…

windows使用python写的YOLO来实现目标识别

使用labelImg标注&#xff0c;YOLO进行目标训练 一、labelImg工具下载及使用1、下载labelImg&#xff08;目标标注工具[【点我下载】](https://github.com/HumanSignal/labelImg)&#xff09;2、使用labelImg 二、下载及使用YOLO1、下载及使用ultralytics&#xff08;volo[点击…

Java——多线程(上)

一 (线程的介绍) 1 多线程的基本概念 (每个进程由三部分构成——>CPU&#xff0c;Data&#xff0c;Code&#xff0c;进程之间完全独立&#xff0c;内存隔离) (运行在进程内的&#xff0c;一个进程可以包含多个线程&#xff0c;线程之间是可以并行的&#xff0c;并且共享相…

SpringBoot3+graalvm:整合并打包为可执行文件

原文网址&#xff1a;SpringBoot3graalvm&#xff1a;整合并打包为可执行文件-CSDN博客 简介 本文介绍SpringBoot3如何整合graalvm&#xff0c;并打包为可执行文件。Windows和Linux都打包。 版本 springboot3.3.6 graalvm21&#xff08;包含JDK21&#xff08;21是最新的LT…

【Bolt.new + PromptCoder】三分钟还原油管主页

【Bolt.new PromptCoder】三分钟还原油管主页 PromptCoder官网&#xff1a;PromptCoder Bolt官网&#xff1a;https://bolt.new/ Bolt 是什么&#xff1f; Bolt.new 是一个提供创建全栈网络应用服务的平台。它允许用户通过提示&#xff08;Prompt&#xff09;、运行&#x…

ubuntu下anconda装pytorch

1、禁用nouveau sudo vim /etc/modprobe.d/blacklist.conf 在文件最后部分插入以下两行内容 blacklist nouveau options nouveau modeset0 更新系统 sudo update-initramfs -u 重启系统 2、装nvidia驱动 卸载原来驱动 sudo apt-get remove nvidia-* &#xff08;若安装…

QT数据库(四):QSqlRelationalTableModel 类

关系数据库概念 例如下列departments、majors、studInfo 这 3 个数据表之间存在关系。 主键与外键 标记“**”的是主键字段&#xff0c;标记“*”的是外键字段。主键字段是一个数据表中表示记录唯一性的字段&#xff0c;例如 studInfo 数据表中的 studID 字段。外键字段是与其…

【Linux】-学习笔记10

第八章、Linux下的火墙管理及优化 1.什么是防火墙 从功能角度来讲 防火墙是位于内部网和外部网之间的屏障&#xff0c;它按照系统管理员预先定义好的规则来控制数据包的进出 从功能实现角度来讲 火墙是系统内核上的一个模块netfilter(数据包过滤机制) …

SpringBoot 手动实现动态切换数据源 DynamicSource (中)

大家好&#xff0c;我是此林。 SpringBoot 手动实现动态切换数据源 DynamicSource &#xff08;上&#xff09;-CSDN博客 在上一篇博客中&#xff0c;我带大家手动实现了一个简易版的数据源切换实现&#xff0c;方便大家理解数据源切换的原理。今天我们来介绍一个开源的数据源…

Crawl4AI:一个为大型语言模型(LLM)和AI应用设计的网页爬虫和数据提取工具实战

这里写目录标题 一、crawl4AI功能及简介1、简介2、特性 二、项目地址三、环境安装四、大模型申请五、代码示例1.生成markdown2.结构化数据 一、crawl4AI功能及简介 1、简介 Crawl4AI 是一个开源的网页爬虫和数据抓取工具&#xff0c;一个python项目&#xff0c;主要为大型语言…

【银河麒麟高级服务器操作系统】有关dd及cp测试差异的现象分析详解

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://documentkylinos.cn dd现象 使用银河麒麟高级服务器操作系统执行两次…

sqli-labs靶场第26-30关

第26关 这关将逻辑运算符&#xff0c;注释符以及空格给过滤了 我们先使用单引号进行闭合 这时我们查看源代码可以看到这一关过滤了很多字符 可以看到这里将or and / -- # 空格等字符都被注释了 空格被过滤了我们可以使用()来代替&#xff0c;and和or可以使用双写来绕过 因为…