一篇文章带你了解Python基础测试工具——UnitTest

测试人员一般使用Python作为主语言脚本来进行自动化开发,而Python自带的UnitTest脚本通常就是测试人员首先掌握的

那么本篇文章我们将来介绍Python的最基本自动化工具UnitTest来开始我们自动化的第一步

我们这篇文章将从以下角度进行讲解:

  • UnitTest基本介绍

  • UnitTest组成部分

  • UnitTest进阶部分

UnitTest基本介绍

首先我们将会对自动化和UnitTest进行一个基本的介绍

自动化脚本介绍

首先我们需要知道什么叫做自动化脚本:

  • 采用代码的形式书写项目,采用项目运行的方式来代替人工测试

自动化脚本的作用主要有以下几点:

  • 解放人力资源,杜绝人为性错误,加快测试速度
  • 使没有代码基本的人可以借助工具快速上手进行自动化测试

UnitTest基本介绍

下面我们来简单介绍一下UnitTest:

  • UnitTest是Python自带的一款单元测试框架,无需安装即写即用

我们来针对上面几个词汇进行讲解:

# 自带框架(官方框架)和第三方框架
# 自带框架:跟随Python官方一共上架的框架,只需要下载Python就可以直接使用,例如Python的UnitTest,OS,requests等
# 第三方框架:由非官方开发人员所开发的可以在Python上导入并使用的框架,例如我们后来会学到的pytest,httpRunner等 # 单元测试
# 单元测试一般是只单单针对后端功能进行测试,而自测环节通常是由后端进行的
# 但是我们测试人员也可以在后端开发期间或前后端联调阶段去进行单元测试以确定后端功能来减少提测时的错误率

最后我们简单介绍一下UnitTest的优点:

  • 能够组织多个⽤例去执⾏
  • 提供丰富的断⾔⽅法
  • 能够⽣成测试报告

UnitTest组成部分

下面我们来正式学习UnitTest的组成部分,来真正学习这个框架

UnitTest组成概述

我们首先在这里简单介绍一下UnitTest的整体组成部分:

# TestCase
# 简称:测试用例(核心模块)
# TestCase是UnitTest里面的一个类名,我们需要创建一个类去继承该TestCase
# 然后我们需要在该类型中书写测试方法def,后续我们就可以直接去执行该TestCase中的Case或采用其他方法集中执行 # TestSuite
# 简称:测试套件
# 正常来说我们的TestCase只能在当前类中去进行执行,如果我们的Case划分写在不同TestCase下,那么我们就不能一同执行
# 但是我们可以采用TestSuite将某个TestCase中的Case加入到该TestSuite中,然后采用执行器一同执行 # TestLoader
# 简称:测试加载器
# TestLoader其实和TestSuite是一样的作用
# 但不同的是TestSuite一次只能添加一个TestCase中的一个Case或一个Testcase的所有Case
# 但是TestLoader可以根据匹配定理来一次性添加多个同一文件夹下的Testcase来实现快速添加的功能 # TestRunner
# 简称:测试执行器
# TestRunner的主要功能就是为了执行TestSuite和TestLoader从而获得对应的测试数据 # Fixture
# 简称:测试夹具
# Fixture的主要功能就是在测试方法执行的前后去添加一些前置条件,比如打开浏览器,打开某个网页等
# Fixture主要被划分为三个级别:文件级别,类级别,方法级别,我们都在下面一一讲述的

TestCase

首先我们来学习UnitTest的核心点TestCase:

# 首先我们来概述TestCase
# TestCase就是一个类,一个需要被我们所继承的类
# TestCase的使用我们下面将采用一个用例来讲解 # 首先我们如果想要TestCase,我们需要导入UnitTest
import unittest # 然后我们需要自定义一个类,这个类需要继承Testcase
class TestDemo(unittest.TestCase):
# 下面我们在该类中所书写的每个方法都会变成一个case,在执行过程中被我们所执行
# 此外我们还需要注意:我们的每个方法都必须以test_开头,否则系统不会将他判断为case def test_method1(self):
print('测试⽅法 1') def test_method1(self):
print('测试⽅法 2') # 正常来说我们的TestCase是可以单独去执行的
# 如果我们想执行该TestCase下的所有方法,我们可以将光标对准Class行,右键run执行,从而获得结果
# 如果我们想执行该TestCase下的某个方法,我们可以将光标对准该def行,右键run执行,从而获得结果

TestRunner&TestSuite&TestLoader

下面我们来一起介绍这三个组件,因为他们之间有非常紧密的联系:

# 首先我们先来介绍TestRunner
# 该类就是一个单纯的执行机器,我们只需要创建对应的实体,然后调用该实体的run方法来执行对应的Case组件即可 # 我们先来介绍TestSuite # 首先我们需要导入unittest
import unittest
# 我们需要注意,如果我们的TestSuite引用到了其他TestCase类的case方法,那么我们需要手动import导入对应类
# 当然这里推荐alt+enter自动导入
from testcase1 import TestDemo1
from testcase2 import TestDemo2
# 然后下面的操作我们可以单独列在一个main方法中,也可以直接书写
if __name__ == '__main__':
# 首先我们创建一个TestSuite
suite = unittest.TestSuite()
# 然后我们需要采用一种格式来添加case到该suite中:suite.addTest(类名('方法名'))
suite.addTest(TestDemo1('test_method1'))
suite.addTest(TestDemo1('test_method2'))
suite.addTest(TestDemo2('test_method1'))
suite.addTest(TestDemo2('test_method2'))
# 当然我们的suite也是存在一种快速导入一个TestCase的所有Case的方法:suite.addTest(unittest.makeSuite(类名))
# 但是我们需要注意makeSuite方法是没有提示信息的,因为该方法其实是unittest之前版本就想要删除的方法,但是推荐使用
suite.addTest(unittest.makeSuite(TestDemo1))
# 最后我们创建一个TestRunner去执行
runner = unittest.TestRunner()
runner.run(suite) # 下面我们来介绍TestLoader # 首先我们需要导入unittest
import unittest
# 然后下面的操作我们可以单独列在一个main方法中,也可以直接书写
if __name__ == '__main__':
# 同理我们需要创建一个TestLoader对象,当然我们也可以直接在创建过程中使用方法然后直接返回TestLoader对象
# TestLoader有这么一个方法:unittest.TestLoader().discover('用例所在的路径', '用例的代码文件名')
# 该用例的代码文件名是可以支持*作为任意字符数,也可以使用?来代替单个字符数
suite = unittest.TestLoader().discover('./case', '*case*.py')
# 同理我们需要一个TestRunner来执行run方法,当然也可以化作一步来执行
unittest.TestRunner().run(suite)

Fixture

最后我们来介绍一下Fixture测试夹具:

# 首先我们需要知道Fixture测试夹具的主要用途就是做一些前置条件或者结束条件,它会在某些特定条件下执行

# 首先是方法级别的Fixture测试夹具
# 它是在每个测试方法(用例代码) 执行前后都会自动调用的结构
# 方法执行之前
def setUp(self):
每个测试方法执行之前都会执行
pass
# 方法执行之后
def tearDown(self):
每个测试方法执行之后都会执行
pass # 然后是针对类级别的Fixture测试夹具
# 它是在每个测试类中所有方法执行前后 都会自动调用的结构(在整个类中执行之前或之后执行一次)
# 需要注意:类级别的Fixture 方法, 是一个 类方法
# 类中所有方法之前
@classmethod
def setUpClass(cls):
pass
# 类中所有方法之后
@classmethod
def tearDownClass(cls):
pass # 最后是针对模块级别的Fixture测试夹具
# 在每个代码文件执行前后执行的代码结构
# 需要注意:模块级别的需要写在类的外边直接定义函数即可
# 代码文件之前
def setUpModule():
pass
# 代码文件之后
def tearDownModule():
pass # 下面我们采用一个用户账户登录的用例来简单展示一下Fixture import unittest # 这些Fixture并没有必要一次性全部书写执行,只需要书写自己所需要的部分即可
# 例如下述的这个Moudle层级的Fixture其实是没有必要执行的,我们可以直接将他省略掉 """""
# 代码文件之前
def setUpModule():
print("文件执行前")
# 代码文件之后
def tearDownModule():
print("文件执行后")
""""" class TestLogin(unittest.TestCase): # 在执行该类前所需要调用的方法
@classmethod
def setUpClass(cls) -> None:
print('------打开浏览器') # 在执行该类后所需要调用的方法
@classmethod
def tearDownClass(cls) -> None:
print('------关闭浏览器') # 每个测试方法执行之前都会先调用的方法
def setUp(self):
print('输入网址......') # 每个测试方法执行之后都会调用的方法
def tearDown(self) -> None:
print('关闭当前页面......') # 测试Case1
def test_1(self):
print('输入正确用户名密码验证码,点击登录 1') # 测试Case2
def test_2(self):
print('输入错误用户名密码验证码,点击登录 2')

UnitTest进阶部分

最后我们介绍UnitTest中的一些进阶知识点

断言

既然讲到Python,那么断言必然是我们所需要了解的部分:

  • 断言就是为了让程序代替人工自动的判断预期结果和实际结果是否相符

断言的结果只分为两种:

  • True:用例通过
  • False:用例不通过,并且抛出异常

我们在这里给出Python中所有经常使用到断言语句:

断言语法 断言意义
assertEqual(预期结果,实际结果) 判断是否相等
assertNotEqual(预期结果,实际结果) 判断是否不等
assertTrue(实际结果) 判断是否为True
assertFalse(实际结果) 判断是否为False
assertIsNone (实际结果) 判断是否为None
assertIsNotNone(实际结果) 判断是否不为None
assertIn(预期结果,实际结果) 判断是否包含

我们给出一个简单的案例来展示断言功能:

# 首先我们需要知道断言其实就是一个判断语句,用来判断assert后面跟随的内容是否为True
# 例如我们简单的计算或比较其实也是一个断言,不过上面的断言语句是我们封装后的结果 # 我们需要注意:如果我们使用到上面封装的assert方法,那么我们需要采用self进行调用 import unittest
from tools import login class TestLogin(unittest.TestCase): # 其实下面的assert就是一个最基本的断言判断
def test_username_password_assert_ok(self):
res = login('admin', '123456')
assert res == "登录成功" # 判断登录:正确的用户名和密码: admin, 123456, 登录成功
def test_username_password_ok(self):
self.assertEqual('登录成功', login('admin', '123456')) # 判断登录:错误的用户名: root, 123456, 登录失败
def test_username_error(self):
self.assertEqual('登录失败', login('root', '123456')) # 判断登录结果是否包含“失败”,若包含即为成功
def test_username_password_error(self):
self.assertIn('失败', login('aaa', '123123'))

参数化

下面我们来介绍UnitTest中的参数化:

  • 在测试方法中, 使用变量来代替具体的测试数据, 然后使用传参的方法将测试数据传递给方法的变量

  • 用于减少代码的重复书写,减少我们的复杂操作

我们在工作中一般会将数据存放在JSON文件中或者从网页中直接导出对应的JSON文件

下面我们来介绍参数化的两种方法:

# unittest 框架本身是不支持 参数化, 想要使用参数化,需要安装插件来完成
# console控制台下载:pip install parameterized # 参数化方法1 import unittest
from parameterized import parameterized
# 首先我们的参数可以存在放类中
# 我们一般采用列表中存在元组或列表来进行存储:[(),(),()]或[[],[],[]]
data = [
('admin','123456','登陆成功'),
('admin','123123','登陆失败'),
('errorAdmin','123456','登陆失败'),
] # 然后我们就可以定义参数化直接调用该data
class TestLogin(unittest.TestCase):
# 采用注解进行注入
@parameterized.expand(data)
def test_login(self,username,password,expect):
self.assertEqual(expect, login(username, password)) # 参数化方法2 # 首先我们需要单独创建一个JSON类型的文件进行数据存储
[
{
"desc": "正确的用户名和密码",
"username": "admin",
"password": "123456",
"expect": "登录成功"
},
{
"desc": "错误的的用户名",
"username": "root",
"password": "123456",
"expect": "登录失败"
},
{
"desc": "错误的的密码",
"username": "admin",
"password": "123123",
"expect": "登录失败"
}
] # 然后我们就可以到TestCase中去书写参数化 # 首先我们需要导入该Json文件以及相关类
import json
import unittest
from parameterized import parameterized
from tools import login # 然后我们需要有一个单独的方法来获取json文件并返回一个类似于我们前面所定义的date:
def build_data():
with open('data.json', encoding='utf-8') as f:
result = json.load(f) # [{}, {}, {}]
data = []
for i in result: # i {}
data.append((i.get('username'), i.get('password'), i.get('expect')))
return data # 剩下的部分其实就和参数化1的操作相同的
class TestLogin(unittest.TestCase):
# 采用注解进行注入
@parameterized.expand(build_data())
def test_login(self,username,password,expect):
self.assertEqual(expect, login(username, password))

跳过测试

我们在前面的TestSuite或TestLoader里面一次性获得所有Case并执行,但有时我们也许不需要执行某些Case:

  • 对于一些未完成的或者不满足测试条件的测试函数和测试类, 不想执行,可以使用跳过

我们下面直接来介绍相关语法:

# 关于跳过测试其实就是一条注解我们就可以实现
# 其具体语法如下 # 直接将测试函数标记成跳过
@unittest.skip('跳过原因') # 根据条件判断测试函数是否跳过 , 判断条件成立, 跳过
@unittest.skipIf(判断条件, '跳过原因') # 我们给出一条简单的案例
import unittest money = 29 class TestDemo(unittest.TestCase): @unittest.skip('无跳过原因')
def test_1(self):
print('跳过测试') @unittest.skipIf(version >= 30, '剩余金钱不足,无法购买')
def test_2(self):
money -= 30
print('剩余金钱足够,可以购买') def test_3(self):
print('可执行方法')

结束语

这篇文章中详细介绍了Python自带的测试框架,因为是自带框架,内容相对比较简单,后续我们会学习pytest来加强自动化开发能力

附录

下面给出我学习和书写该篇文章的一些参考文章,大家也可以去查阅:

  1. 黑马课程:11.Python-unittest 的组成_哔哩哔哩_bilibili

  2. 菜鸟教程:Python 基础教程 | 菜鸟教程 (runoob.com)

一篇文章带你了解Python基础测试工具——UnitTest的更多相关文章

  1. 一篇文章带你掌握主流基础框架——Spring

    一篇文章带你掌握主流基础框架--Spring 这篇文章中我们将会介绍Spring的框架以及本体内容,包括核心容器,注解开发,AOP以及事务等内容 那么简单说明一下Spring的必要性: Spring技 ...

  2. 【万字长文】别再报班了,一篇文章带你入门Python

    本文始发于个人公众号:TechFlow,原创不易,求个关注 最近有许多小伙伴后台联系我,说目前想要学习Python,但是没有一份很好的资料入门.一方面的确现在市面上Python的资料过多,导致新手会不 ...

  3. 万字长文,一篇文章带你入门Python

    注释 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去学习更加高深的知识.那么针对这三类人,我给大家提供 ...

  4. 一篇文章带你用Python网络爬虫实现网易云音乐歌词抓取

    前几天小编给大家分享了数据可视化分析,在文尾提及了网易云音乐歌词爬取,今天小编给大家分享网易云音乐歌词爬取方法. 本文的总体思路如下: 找到正确的URL,获取源码: 利用bs4解析源码,获取歌曲名和歌 ...

  5. 一篇文章教会你利用Python网络爬虫获取电影天堂视频下载链接

    [一.项目背景] 相信大家都有一种头疼的体验,要下载电影特别费劲,对吧?要一部一部的下载,而且不能直观的知道最近电影更新的状态. 今天小编以电影天堂为例,带大家更直观的去看自己喜欢的电影,并且下载下来 ...

  6. MYSQL(基本篇)——一篇文章带你走进MYSQL的奇妙世界

    MYSQL(基本篇)--一篇文章带你走进MYSQL的奇妙世界 MYSQL算是我们程序员必不可少的一份求职工具了 无论在什么岗位,我们都可以看到应聘要求上所书写的"精通MYSQL等数据库及优化 ...

  7. 一篇文章带你掌握主流数据库框架——MyBatis

    一篇文章带你掌握主流数据库框架--MyBatis MyBatis 是一款优秀的持久层框架,它支持自定义 SQL.存储过程以及高级映射. 在之前的文章中我们学习了MYSQL和JDBC,但是这些东西远远不 ...

  8. 一篇文章带你掌握主流办公框架——SpringBoot

    一篇文章带你掌握主流办公框架--SpringBoot 在之前的文章中我们已经学习了SSM的全部内容以及相关整合 SSM是Spring的产品,主要用来简化开发,但我们现在所介绍的这款框架--Spring ...

  9. 一篇文章带你掌握MyBatis简化框架——MyBatisPlus

    一篇文章带你掌握MyBatis简化框架--MyBatisPlus 我们在前面的文章中已经学习了目前开发所需的主流框架 类似于我们所学习的SpringBoot框架用于简化Spring开发,我们的国人大大 ...

  10. 一篇文章带你了解设计模式原理——UML图和软件设计原则

    一篇文章带你了解设计模式原理--UML图和软件设计原则 我们在学习过程中可能并不会关心设计模式,但一旦牵扯到项目和面试,设计模式就成了我们的短板 这篇文章并不会讲到二十三种设计模式,但是会讲解设计模式 ...

随机推荐

  1. Sa-Token 多账号认证:同时为系统的 Admin 账号和 User 账号提供鉴权操作

    Sa-Token 是一个轻量级 java 权限认证框架,主要解决登录认证.权限认证.单点登录.OAuth2.微服务网关鉴权 等一系列权限相关问题. Gitee 开源地址:https://gitee.c ...

  2. Linux: rsyslog.conf 配置

    refer to: https://www.debian.org/doc/manuals/debian-handbook/sect.syslog.en.html 日志子系统 Each log mess ...

  3. Mysql高级2-SQL性能分析

    一.SQL执行频率 MySQL客户端 连接成功后,通过show [session | global] status 命令可以提供服务器状态信息,通过如下指令,可以查看当前数据库的insert,upda ...

  4. Redis从入门到放弃(2):数据类型

    在Redis中,数据以键值对的形式存储.Redis支持五种主要的数据类型,每种类型都有不同的用途和特性. 本文将介绍Redis的五种数据类型:字符串(string),哈希(hash),列表(list) ...

  5. 记一次使用pagehelper的坑(返回的total和size每页条数一致的问题)

    问题描述 众所周知,pagehelper使用时应该在dao查询语句的前一句加上PageHelper.startPage,所以标题的问题由此引出-- 原因 PageHelper.startPage使用后 ...

  6. 基于C#的无边框窗体阴影绘制方案 - 开源研究系列文章

    今天介绍无边框窗体阴影绘制的内容. 上次有介绍使用双窗体的方法来显示阴影,这次介绍使用API函数来进行绘制.这里使用的是Windows API函数,操作系统的窗体也是用的这个来进行的绘制. 1. 项目 ...

  7. 【Windows】KMS 激活命令记录

    目录 KMS 服务器激活 Office.Visio 推荐使用 office tool plus 部署并配置 KMS 激活 什么是 KMS? KMS 正版与否的区别 总结 KMS 服务器激活 利用 KM ...

  8. 系统内存管理:虚拟内存、内存分段与分页、页表缓存TLB以及Linux内存管理

    虚拟内存 虚拟内存是一种操作系统提供的机制,用于将每个进程分配的独立的虚拟地址空间映射到实际的物理内存地址空间上.通过使用虚拟内存,操作系统可以有效地解决多个应用程序直接操作物理内存可能引发的冲突问题 ...

  9. JOIN 关联表中 ON、WHERE 后面跟条件的区别

    SQL中join连接查询时条件放在on后与where后的区别 数据库在通过连接两张或多张表来返回记录时,都会生成一张中间的临时表,然后再将这张临时表返回给用户. 在使用left jion时,on和wh ...

  10. Elasticsearch整合SpringBoot案例

    1.elasticsearch官方文档的使用与介绍 1.1.Rest客户端初始化官方文档链接: https://www.elastic.co/guide/en/elasticsearch/client ...