从零开始学习Python中UnitTest测试框架:实现高效自动化测试流程

news2025/2/26 20:36:55

目录:导读

引言

1.白盒测试原理

2.自动化测试用例编写

3.UnitTest测试框架

3.1UnitTest组件(测试固件)

3.1.2测试套件

3.1.3测试运行

3.1.4测试断言

3.1.5测试结果

3.2unittest测试固件的详解

3.2.1测试固件每次均执行

3.2.2测试固件只执行一次

3.2.3。测试用例执行的顺序详解

3.3编写测试用例注意的事项

3.4测试套件详解

3.4.1按测试类来执行

3.4.2按测试模块执行

3.4.3按具体的测试用例来执行

3.4.4自定义测试套件

3.4.5分离测试套件

3.5 测试报告

3.6加载所有的测试模块

3.7生成HTML测试报告


引言

Python是一种优秀的编程语言,可以适用于不同方面的开发需求。在软件测试领域,Python也有着非常出色的表现,特别是其自带的UnitTest测试框架诸如单元测试、集成测试和功能测试等各种类型的测试都可以通过这个框架进行实现

本文将为您介绍Python中的UnitTest测试框架,并且展示如何使用它来构建一个完整的测试流程。

文章不仅会详细讲解测试框架的基本概念及其应用场景,还会介绍如何编写高效的测试脚本、如何使用第三方工具来扩展测试框架的功能、以及如何对测试结果进行分析和报告。

凭借本文,您将能够全面掌握Python中UnitTest测试框架的全栈应用,从而更好地完成自动化测试任务并提高软件质量。

1.白盒测试原理

简单的来说就是一个白色的盒子,可以直接看见内部的代码来进行测试

2.自动化测试用例编写

不管基于什么测试的框架,自动化测试用例的编写都要遵循一下规则

测试方法

1、初始化

2、执行

3、验证

4、清理

在转到被测系统当中

3.UnitTest测试框架

3.1UnitTest组件(测试固件)

UnitTest是属于Python语言的单元测试框架,它的核心组件具体合一总结为如下:

测试类继承unittest模块中的TestCase类后,依据继承的这个类来设置一个信的测试用例类和测试方法,案例代码:

 1 from selenium import webdriver
 2 import unittest
 3 
 4 class Init(unittest.TestCase):
 5     def setUp(self) -> None:
 6         self.driver=webdriver.Chrome()
 7         self.driver.maximize_window()
 8         self.driver.get('https://mail.sina.com.cn/')
 9         self.driver.implicitly_wait(30)
10 
11     def tearDown(self) -> None:
12         self.driver.quit()

该代码为测试固件

3.1.2测试套件

测试套件顾名思义就是相关测试用例的合计。在unittest中主要通过testSuite类提供对测试套件的支持,代码如下

 1 from page.login import Login
 2 from utils.operationJson import readJson
 3 from page.init import Init
 4 class LoginTest(Init,Login):
 5 
 6     def test_sina_login_username(self):
 7         '''登录验证:验证账户为空的错误提示信息'''
 8         self.userName(value='')
 9         self.clickLogin()
10         self.assertEqual(self.getUsernameDiv(),readJson()['emailNull'])
11 
12     def test_sina_login_emailError(self):
13         '''登录验证:邮箱格式不正确的错误提示'''
14         self.userName(value='asdiwdsi')
15         self.clickLogin()
16         self.assertEqual(self.emailError01(),readJson()['emailError01'])

3.1.3测试运行

管理和运行测试用例的对象

3.1.4测试断言

对所测试的对象依据返回的实际结果与期望结果进行断言校验

3.1.5测试结果

测试结果类管理着测试结果的输出, 测试结果呈现给最终的用户来反馈本次测试执行的结果信息。

3.2unittest测试固件的详解

在unittest中的测试固件依据方法可以分为两种方式执行,一种是测试固件只执行一次,另外一种是测试固件每次都执行,下面依据具体的案例来讲解二者。

3.2.1测试固件每次均执行

1 from selenium import webdriver
 2 import unittest
 3 
 4 class Init01(unittest.TestCase):
 5     def setUp(self) -> None:
 6         self.driver=webdriver.Chrome()
 7         self.driver.maximize_window()
 8         self.driver.get('https://mail.sina.com.cn/register/regmail.php')
 9         self.driver.implicitly_wait(30)
10 
11     def tearDown(self) -> None:
12         self.driver.quit()

3.2.2测试固件只执行一次

使用的是类方法,这样测试固件只会执行一次的

 1 import  unittest
 2 from selenium import  webdriver
 3 
 4 class ApiTest(unittest.TestCase):
 5     @classmethod
 6     def setUpClass(cls):
 7         cls.driver=webdriver.Chrome()
 8         cls.driver.maximize_window()
 9         cls.driver.get('http://www.baidu.com')
10         cls.driver.implicitly_wait(30)
11         
12     @classmethod
13     def tearDownClass(cls):
14         cls.driver.quit()
15         
16     def test_baidu_title(self):
17         self.assertEqual(self.driver.title,'百度一下,你就知道')
18         
19     def test_baidu_url(self):
20         self.assertEqual(self.driver.current_url,'https://www.baidu.com/')

3.2.3。测试用例执行的顺序详解

在unittest中,测试点的执行顺序是依据ascill码来执行的,也就是说根据ASCLL码的加载顺序,数字与字母的顺序

优先数字 然后大写字母 、小写字母  切记数字的大笑之不能包含test,值的是test后面的测试点的数字大小,

1 import  unittest
2 class WebUI(unittest.TestCase):
3     def test_001(self):
4         pass
5     
6 def test_002(self):
7     pass

由上述代码可见 代码的执行顺序为 001 002 当然测试点不单纯是数字的,也会有字符串相关的,在Python中,字符串与数字转换为:

chr():数字转为字母

ord():字母转为数字

同时也会有字符串与数字的比较

3.3编写测试用例注意的事项

1、在一个测试类里面,每一个测试方法都是以test开头的,test不能是中间或者尾部,必须是开头

2、每一个测试用例方法都应该有注释信息,这样在测试报告就会显示具体的测试点的检查点

3、在自动化测试中,每个测试用例都必须得有断言,五断言的自动化测试用例都是无效的

4、最好一个测试用例方法对应一个业务测试点,不要多个业务检查点写一个测试用例

5、如果设计到业务逻辑的处理,最好把业务逻辑的处理方法在断言面前,这样做的目的是不要因为业务逻辑 行错误导致断言也是失败

6、测试用例名称最好是规范,有约束

7、是否先写自动化测试的测试代码,在使用自动化测试方式写,本人觉得没必要,毕竟能够做自动化测试的都具备了功能测试的基本水平,所以没必要把一个业务的检查点写多次,浪费时间和人力成本

3.4测试套件详解

UnitTest的测试框架中提供了很多丰富的测试套件,所谓测试套件其实我们可以把它理解成为测试用例的集合,或者可以说理解为一个容器,这个容器里面有很多测试用例、
各个不同测试套件的应用和实战

3.4.1按测试类来执行

按测试类执行,可以理解成为在测试套件中,我们按测试类的方式来执行,那么也就不需要在乎一个测试类里面有多少测试用例,我们是以测试类为单位来进行执行,测试类里面有多少的测试用例,我们都会进行执行

 1 import  unittest
 2 from selenium import  webdriver
 3 from test.init import Init
 4 
 5 class Baidu(Init):
 6 
 7     def test_baidu_shouye_title(self):
 8         '''验证百度首页的title信息'''
 9         assert self.driver.title=='百度一下,你就知道'
10 
11     def test_baidu_shouye_url(self):
12         '''验证百度的首页URL地址'''
13         assert self.driver.current_url=='https://www.baidu.com/'

3.4.2按测试模块执行

思维按测试模块来执行,就是以模块为单位来进行执行,那么其实在一个模块里面可以编写很多的类,下面通过详细的代码演示这部分,具体案例代码如下:

1 import  unittest
 2 from selenium import  webdriver
 3 from test.init import Init
 4 
 5 class Baidu(Init):
 6    def test_baidu_shouye_title(self):
 7       '''验证百度首页的title信息'''
 8       assert self.driver.title=='百度一下,你就知道'
 9 
10    def test_baidu_shouye_url(self):
11       '''验证百度的首页URL地址'''
12       assert self.driver.current_url=='https://www.baidu.com/'
13 
14 
15 class BaiDuSo(Init):
16    def test_baidu_so_value(self):
17       '''百度搜索关键字的验证'''
18       so=self.driver.find_element_by_id('kw')
19       so.send_keys('Selenium4')
20       assert so.get_attribute('value')=='Selenium4'

3.4.3按具体的测试用例来执行

当然如果是仅仅执行某一个测试用例,执行的方式一种是鼠标放到具体的测试用例,然后右键执行就可以了,另外一种方式是我们可以把需要执行的测试用例添加到测试套件中,然后来单独的进行执行,这种方式其实我个人是不建议的,但是还是通过具体的代码来演示:

1 import  unittest
 2 from selenium import  webdriver
 3 from test.init import Init
 4 
 5 class Baidu(Init):
 6 
 7    def test_baidu_shouye_title(self):
 8       '''验证百度首页的title信息'''
 9       assert self.driver.title=='百度一下,你就知道'
10 
11    def test_baidu_shouye_url(self):
12       '''验证百度的首页URL地址'''
13       assert self.driver.current_url=='https://www.baidu.com/'

3.4.4自定义测试套件

针对测试套件的方式是很多的,那么我们就可以把加载所有测试用例的方法单独分离出来了,当然其实是可以的,这样我们只需要关注更多的测试用例的执行,下面具体演示测试套件的分离部分

 1 import  unittest
 2 from selenium import  webdriver
 3 from test.init import Init
 4 
 5 
 6 class Baidu(Init):
 7 
 8    def test_baidu_shouye_title(self):
 9       '''验证百度首页的title信息'''
10       assert self.driver.title=='百度一下,你就知道'
11 
12    def test_baidu_shouye_url(self):
13       '''验证百度的首页URL地址'''
14       assert self.driver.current_url=='https://www.baidu.com/'
15 
16    def suite(self):
17       '''自定义测试套件'''
18       return unittest.TestLoader().loadTestsFromModule('test_customer.py')

3.4.5分离测试套件

在一个完整的自动化测试用例中,比如在UI的自动化测试用力中,我们的测试用例是按照业务模块来进行划分的,那么以为我们着想需要编写很多的模块,但是就存在重复的代码,比如我们针对百度的产品进行测试,不管是测试什么模块,测试固件这部分的代码每个测试模块都是一样的,这样就导致很多重复的代码,重复必然就带来测试效率低下的问题,举一个简单的问题,比如需要修改测试的地址,就需要修改很多的测试模块,但是如果把测试套件分离出来,我们就需要该一个地方就可以了,这样我们的测试效率就提升了一点,毕竟效率的提升是需要做很多的,不可能一点就进行大幅度的提升。分离测试套件的思想其实很简单,就是运用了继承的思想来解决这个问题,我们将分离的测试固件放到init.py里面

1 import unittest
 2 from selenium import  webdriver
 3 
 4 
 5 class Init(unittest.TestCase):
 6    def setUp(self) -> None:
 7       self.driver=webdriver.Chrome()
 8       self.driver.maximize_window()
 9       self.driver.implicitly_wait(30)
10       self.driver.get('http://www.baidu.com')
11 
12    def tearDown(self) -> None:
13       self.driver.quit()

这样其他测试模块就需要引入这个模块中的init类就可以了,然后在继承这个类,具体代码如下:

 1 import  unittest
 2 from selenium import  webdriver
 3 from test.init import Init
 4 
 5 class Baidu(Init):
 6 
 7    def test_baidu_shouye_title(self):
 8       '''验证百度首页的title信息'''
 9       assert self.driver.title=='百度一下,你就知道'
10 
11    def test_baidu_shouye_url(self):
12       '''验证百度的首页URL地址'''
13       assert self.driver.current_url=='https://www.baidu.com/'

其实过程中我们使用了很简单的思维,但是解决了一个很核心的问题

3.5 测试报告

下面详细的说明下测试报告的生成以及加载所有模块的过程,我们在tests的模块下编写了很多的测试用例,但是实际的生产环境中总不能按照测试模块来执行,我们都是加载所有的测试模块来执行并且最终生成基于HTML的测试报告,测试报告会用到第三方的库HTMLTestRunner

3.6加载所有的测试模块

下面我们编写具体的函数来加载所有的测试模块,路径处理部分我们使用os的模块来进行处理,针对路径处理的这部分特别的再说下,不能使用硬编码,使用硬编码只会带来维护的成本性,而且也涉及到不同的操作系统针对路径是有不同的,比如MAC和Linux下是没有C盘的,但是Windows操作系统,这部分需要特别的注意下,下面的函数主要体验的是加载所有测试模块的代码,具体如下:

 1 import  os
 2 import  HTMLTestRunner
 3 import  unittest
 4 
 5 
 6 def base_dir():
 7    return os.path.join(os.path.dirname(__file__),'test')
 8 
 9 
10 def getSuite():
11    tests=unittest.defaultTestLoader.discover(
12       start_dir=base_dir(),
13       pattern='test_*.py',
14       top_level_dir=None
15    )
16    return tests

从如上的输出结果来看,已经到模块级别了,下面我们出一个需求,总共有多少测试用例,请统计出来,其实我们可以针对模块进行再次进行循环到类别,然后到测试用例的级别,然后把所有的测试用例加到一个列表里面,获取列表的长度就是测试用例的总数,下面是到类级别的代码和输出结果:

1 if __name__ == '__main__':
2    for item in getSuite():
3       for i in item:
4          print(i)
 1 import  os
 2 import  HTMLTestRunner
 3 import  unittest
 4 
 5 
 6 def base_dir():
 7     return os.path.join(os.path.dirname(__file__),'test')
 8 
 9 
10 def getSuite():
11     tests=unittest.defaultTestLoader.discover(
12         start_dir=base_dir(),
13         pattern='test_*.py',
14         top_level_dir=None
15     )
16     return tests
17 
18 
19 
20 if __name__ == '__main__':
21     for item in getSuite():
22         for i in item:
23             for j in i:
24                 print(j)

3.7生成HTML测试报告

下面具体展示测试报告的生成,把测试报告储存到report的文件夹里面,思考每次生成的测试报名名称一致,我们可以以当前时间作为区分,那么整体实现的代码如下:

 1 import  os
 2 import  HTMLTestRunner
 3 import  unittest
 4 import time
 5 
 6 
 7 def base_dir():
 8     return os.path.join(os.path.dirname(__file__),'test')
 9 
10 
11 def getSuite():
12     tests=unittest.defaultTestLoader.discover(
13         start_dir=base_dir(),
14         pattern='test_*.py',
15         top_level_dir=None
16     )
17     return tests
18 
19 
20 
21 def getNowTime():
22     return time.strftime('%y-%m-%d %h_%m_%s',time.localtime(time.time()))
23 
24 def run():
25     filename=os.path.join(os.path.dirname(__file__),'report',getNowTime()+'report.html')
26     fp=open(filename,'wb')
27     runner=HTMLTestRunner.HTMLTestRunner(
28         stream=fp,
29         title='',
30         description=''
31     )
32     runner.run(getSuite())

在report的文件夹下,生成的测试报告打开后显示如下:

结语

这篇贴子到这里就结束了,最后,希望看这篇帖子的朋友能够有所收获。欢迎留言,或是关注我的专栏和我交流。

有兴趣就点击下方小卡片去和大神交流交流吧!!

 

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

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

相关文章

【JavaEE】CAS机制(比较并交换)

哈喽,大家好~我是你们的老朋友保护小周ღ,本期为大家带来的是 CAS (compare and swap) 比较并交换,CAS 是物理层次支持程序的原子操作,CAS 是一种完全不同于 synchronized 锁保证多线程安全问题的机制,可以用来进行无锁…

Java基础——Stream流

(1)Stream流概述: 1.什么是Stream流? 用于简化集合和数组操作的API。结合了Lambda表达式。方便操作集合/数组的手段(集合/数组才是开发的目的)。2.体验Stream流的作用: import java.util.Arr…

高并发场景I/O优化

大家好,我是易安! Java I/O是一个众所周知的概念。它常被用于读写文件、实现Socket信息传输等操作,这些都是系统中最常见的与I/O相关的任务。 我们都了解,I/O的速度相较于内存速度较慢。在当前大数据时代背景下,I/O性能…

java足球体育新闻资讯发布系统ssh

为 本系统的功能目标分为以下几个模块:用户管理模块、足球新闻类别管理模块、足球新闻管理模块、留言管理模块和前台足球新闻浏览模块。 系统功能模块的划分,是在需求分析基础上进行的,是把具有复杂功能的系统通过设计分解为具有基本独立&…

【并发编程】AQS源码

ReentrantLock 互斥锁,可重入 AQS是可以支持互斥锁和共享锁的,这里只分析互斥锁的源码 加锁 公平锁和非公平锁 公平锁 final void lock() {acquire(1); //抢占1把锁.}// AQS里面的方法public final void acquire(int arg) { if (!tryAcquire(arg) &&acq…

MySQL:事务、索引、用户管理、备份、数据库设计(三大范式)

文章目录Day 03:一、事务1. 原则2. 测试实现二、索引1. 分类2. 创建索引3. 分析 sql 执行的状况4. 测试索引5. 索引原则三、数据库用户管理四、备份五、规范数据库设计1. 三大范式注意:Day 03: 一、事务 事务(transaction):要么…

含光热电站、有机有机朗肯循环、P2G的综合能源优化调度(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

『pyqt5 从0基础开始项目实战』04. 表格数据的初始化(保姆级图文)

目录导包和框架代码准备json数据文件表格数据接入1. 准备文件路径2. 读取json数据3. 将得到的json数据放入table设置单元格不可修改把数据中的数字转为映射内容完整代码总结欢迎关注 『pyqt5 从0基础开始项目实战』 专栏,持续更新中 欢迎关注 『pyqt5 从0基础开始项…

TiDB实战篇-备份恢复策略

简介 简要说明TiDB备份恢复策略。 备份的类型 热备 TiDB使用MVCC机制实现设备的。 冷备 需要停机备份。 温备 备份的时候只能读不能够写。 备份技术 逻辑备份 物理备份 物理备份的限制 基于复制的备份 复制恢复是最快的。(TiDB CDC,TiDB Binlog&#xff…

【C语言】函数详解(嵌套调用和链式访问、声明及定义、递归)

简单不先于复杂,而是在复杂之后。 目录 1.函数的嵌套调用和链式访问 1.1 嵌套调用 1.2 链式访问 2. 函数的声明和定义 2.1 函数声明 2.2 函数定义 3. 函数递归 3.1 什么是递归? 3.2 递归的两个必要条件 3.2.1 练习1(需要画图…

Spring Security实战(三)—— 自动登录与注销登录

目录 一、实现自动登录 1. 散列加密方案 2. 持久化令牌方案 二、注销登录 一、实现自动登录 自动登录是将用户的登录信息保存在用户浏览器的cookie中,当用户下次访问时,自动实现校验并建立登录态的一种机制。 Spring Security 提供了两种非常好的令牌&a…

C ++ 基础入门。加强变量、指针、结构体理解

1、 const放外面,值不可以改。只读 同理于指针 看const右侧紧跟着的是指针还是常量, 是指针就是常量指针,是常量就是指针常量 const 放外面,值不可以改 2、 所有的指针类型,包括结构体指针 double * int *都是和操作系统位数…

补充——spark RDD序列化和持久化

目录 RDD序列化 闭包检查: 序列化方法和属性 Kryo序列化框架: RDD持久化(RDD persistence) RDDCache缓存 RDD persist缓存 什么时候使用persist()? RDD CheckPoint 检查点 缓存和检查点区别 RDD序列化 闭包检查&#x…

JavaScript 的基础函数有哪些?

1、在 JavaScript 中将数组本地转换为对象 JavaScript 有一个原生函数 Object.fromEntries,可用于将任何输入数组转换为对象。 1.const anArray [ 2. [firstname, Paul], 3. [surname, Knulst], 4. [address, worldwide], 5. [role, Senior Engineer], 6. […

Java中的异常Exception和捕获,自定义异常

文章目录1. 异常概述1.1 什么是程序的异常1.2 异常的抛出机制1.3 如何对待异常2. Java异常体系2.1 Throwable2.2 Error 和 Exception2.3 编译时异常和运行时异常3. 常见的错误和异常3.1 Error3.2 运行时异常3.3 编译时异常4. 异常的处理4.1 异常处理概述4.2 捕获异常&#xff0…

springboot整合websocket

1.创建springboot项目&#xff0c;引入spring-boot-starter-websocket依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>全部依赖如下&#xff1a; &l…

JDBC之DAO层封装思想超详解

Mysql版本&#xff1a;8.0.26 可视化客户端&#xff1a;sql yog 编译软件&#xff1a;IntelliJ IDEA 2019.2.4 x64 运行环境&#xff1a;win10 家庭中文版 jdk版本&#xff1a;1.8.0_361 目录一、DAO是什么&#xff1f;二、案例演示2.1 准备数据2.2 创建bean包2.3 建立DAO包2.2…

Houdini>RBD(搅拌大米效果)并导出FBX到unity

Houdini&#xff1e;RBD(搅拌大米效果) 效果展示&#xff1a; 动图录制软件&#xff1a;Cockos Incorporated | LICEcap 参考链接&#xff1a;导出除了ABC外&#xff0c;比较小的FBX文件用法 目录&#xff1a; 一、引用模型的处理&#xff1a; 1、大米 模型创建 多层复制 …

Mybatis(六)缓存

缓存是Mybatis中非常重要的特性&#xff0c;Mybatis的一级缓存基于SqlSession实现&#xff0c;二级缓存基于Mapper实现。 一、缓存的使用 一级缓存默认开启&#xff0c;Mybatis提供了一个配置参数localCacheScope来控制一级缓存的级别&#xff0c;该参数的取值可以是session、…

【机器学习】P10 从头到尾实现一个线性回归案例

这里写自定义目录标题&#xff08;1&#xff09;导入数据&#xff08;2&#xff09;画出城市人口与利润图&#xff08;3&#xff09;计算损失值&#xff08;4&#xff09;计算梯度下降&#xff08;5&#xff09;开始训练&#xff08;6&#xff09;画出训练好的模型&#xff08;…