28. Flask 使用unittest进行单元测试

2024-08-21 03:58

本文主要是介绍28. Flask 使用unittest进行单元测试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

为什么要测试?

Web程序开发过程一般包括以下几个阶段:需求分析,设计阶段,实现阶段,测试阶段。其中测试阶段通过人工或自动来运行测试某个系统的功能。目的是检验其是否满足需求,并得出特定的结果,以达到弄清楚预期结果和实际结果之间的差别的最终目的。

测试的分类:

测试从软件开发过程可以分为:单元测试、集成测试、系统测试等。在众多的测试中,与程序开发人员最密切的就是单元测试,因为单元测试是由开发人员进行的,而其他测试都由专业的测试人员来完成。所以作为开发人员主要需要学习单元测试。

什么是单元测试?

程序开发过程中,写代码是为了实现需求。当我们的代码通过了编译,只是说明它的语法正确,功能能否实现则不能保证。 因此,当我们的某些功能代码完成后,为了检验其是否满足程序的需求。可以通过编写测试代码,模拟程序运行的过程,检验功能代码是否符合预期。

单元测试就是开发者编写一小段代码,检验目标代码的功能是否符合预期。通常情况下,单元测试主要面向一些功能单一的模块进行。

举个例子:一部手机有许多零部件组成,在正式组装一部手机前,手机内部的各个零部件,CPU、内存、电池、摄像头等,都要进行测试,这就是单元测试。

在Web开发过程中,单元测试实际上就是一些“断言”(assert)代码。

断言就是判断一个函数或对象的一个方法所产生的结果是否符合你期望的那个结果。 python中assert断言是声明布尔值为真的判定,如果表达式为假会发生异常。单元测试中,一般使用assert来断言结果。

断言方法的使用:
# 定义一个list
In [6]: a = [1,3,5,7,9]In [7]: b = 3# 断言判断 b 是否存在 a 中,如果正确,则不会报错
In [8]: assert b in a# 断言如果报错,可以自定义打印错误信息,这里定义错误为 False
In [9]: assert b not in a, 'False'
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-9-a3961e666e68> in <module>
----> 1 assert b not in a, 'False'AssertionError: False# 断言报错,但是如果没有自定义错误信息,则只会显示错误
In [10]: assert b not in a
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-10-57ad4b59ee88> in <module>
----> 1 assert b not in aAssertionError:In [11]: 

断言语句类似于:

if not expression:raise AssertionError

常用的断言方法:

assertEqual     如果两个值相等,则pass
assertNotEqual  如果两个值不相等,则pass
assertTrue      判断bool值为True,则pass
assertFalse     判断bool值为False,则pass
assertIsNone    不存在,则pass
assertIsNotNone 存在,则pass

如何测试?

写一个斐波那契数列 Fibonacci 来进行测试,验证以下的数字是否符合斐波那契数列

可以测试的数字:

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,
# 斐波那契数列 Fibonacci
def fibo(x):if x == 0:resp = 0elif x == 1:resp = 1else:return fibo(x-1) + fibo(x-2)return resp
assert fibo(5) == 5

测试执行判断断言如下:

13423234-9bb6d852954ab268.png

单元测试的基本写法:

首先,定义一个类,继承自unittest.TestCase

import unittest
class TestClass(unitest.TestCase):pass

其次,在测试类中,定义两个测试方法

import unittest
class TestClass(unittest.TestCase):#该方法会首先执行,方法名为固定写法def setUp(self):pass#该方法会在测试代码执行完后执行,方法名为固定写法def tearDown(self):pass

最后,在测试类中,编写测试代码

import unittest
class TestClass(unittest.TestCase):#该方法会首先执行,相当于做测试前的准备工作def setUp(self):pass#该方法会在测试代码执行完后执行,相当于做测试后的扫尾工作def tearDown(self):pass#测试代码def test_app_exists(self):pass

看清楚了上面关于unittest测试框架的基本写法之后,下面来写一个登录的视图函数,然后再写一个视图函数的单元测试。

登录视图函数的单元测试

1.编写一个模拟登录的视图函数 login.py

from flask import Flask, request, jsonifyapp = Flask(__name__)@app.route("/login", methods=["POST"])
def login():"""登录"""name = request.form.get("name")password = request.form.get("password")# ""  0  [] () {} None 在逻辑判断时都是假if not all([name, password]):# 表示name或password中有一个为空或者都为空return jsonify(code=65535, message="参数不完整")if name == "admin" and password =="123456":return jsonify(code=0, message="OK")else:return jsonify(code=65535, message="用户名或密码错误")if __name__ == '__main__':app.run(debug=True)

2. 使用postman测试login登录

首先输入正确的用户名和密码测试,如下:

13423234-f5153d035115192d.png

然后去除用户名或者密码,缺少参数进行请求,如下:

13423234-5a797654398ecdea.png

故意输错密码进行请求,如下:

13423234-eda3ac12e5c86887.png

通过postman测试接口这三种情况是可以的,但是如果每次都要手动去进行这样的单元测试,就会感觉很麻烦了。

那么下面可以将这三种情况写成单元测试的代码,来避免重复测试。

3.编写单元测试代码 test_login.py

import unittest
from login import app
import jsonclass TestLogin(unittest.TestCase):"""定义测试案例"""def setUp(self):"""在执行具体的测试方法前,先被调用"""self.app = app# 激活测试标志app.config['TESTING'] = True# 可以使用python的http标准客户端进行测试# urllib  urllib2  requests# 在这里,使用flask提供的测试客户端进行测试self.client = app.test_client()def test_empty_name_password(self):"""测试模拟场景,用户名或密码不完整"""# 使用客户端向后端发送post请求, data指明发送的数据,会返回一个响应对象response = self.client.post("/login", data={})# respoonse.data是响应体数据resp_json = response.data# 按照json解析resp_dict = json.loads(resp_json)# 使用断言进行验证:是否存在code字符串在字典中self.assertIn("code", resp_dict)# 获取code的返回码的值,验证是否为错误码 65535code = resp_dict.get("code")self.assertEqual(code, 65535)# 测试只传nameresponse = self.client.post("/login", data={"name": "admin"})# respoonse.data是响应体数据resp_json = response.data# 按照json解析resp_dict = json.loads(resp_json)# 使用断言进行验证self.assertIn("code", resp_dict)# 验证错误码 65535code = resp_dict.get("code")self.assertEqual(code, 65535)# 验证返回信息msg = resp_dict.get('message')self.assertEqual(msg, "参数不完整")def test_wrong_name_password(self):"""测试用户名或密码错误"""# 使用客户端向后端发送post请求, data指明发送的数据,会返回一个响应对象response = self.client.post("/login", data={"name": "admin", "password": "123456789"})# respoonse.data是响应体数据resp_json = response.data# 按照json解析resp_dict = json.loads(resp_json)# 使用断言进行验证self.assertIn("code", resp_dict)# 验证错误码code = resp_dict.get("code")self.assertEqual(code, 65535)# 验证返回信息msg = resp_dict.get('message')self.assertEqual(msg, "用户名或密码错误")if __name__ == '__main__':unittest.main()

执行测试如下:

13423234-5bea14e09e4e1706.png

从上面可以看出,大部分的Flask框架的单元测试就是这样的处理流程。下面再提供一个数据库单元测试的示例。

数据库单元测试:

数据单元测试的基本步骤方法如下:
1.替换使用一个创建的testdb测试库,避免影响项目的实际数据库
2.导入代码中构建数据库的模型类、app、db等对象,创建数据库以及创建数据
3.断言查询数据库的数据,正确则单元测试成功
4.测试完毕之后,删除创建的数据表

下面来看看实际代码,如下:

准备用来测试的项目代码 db_database.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import pymysql
pymysql.install_as_MySQLdb()
from flask_migrate import Migrate,MigrateCommand
from flask_script import Shell,Managerapp = Flask(__name__)
manager = Manager(app)class Config(object):"""配置参数"""# 设置连接数据库的URLuser = 'root'password = '***************'database = 'flask_ex'app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://%s:%s@127.0.0.1:3306/%s' % (user,password,database)# 设置sqlalchemy自动更跟踪数据库SQLALCHEMY_TRACK_MODIFICATIONS = True# 查询时会显示原始SQL语句# app.config['SQLALCHEMY_ECHO'] = True# 禁止自动提交数据处理app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = False# 设置密钥,用于csrf_token的加解密app.config["SECRET_KEY"] = "xhosd6f982yfhowefy29f"# 读取配置
app.config.from_object(Config)# 创建数据库sqlalchemy工具对象
db = SQLAlchemy(app)#第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例
migrate = Migrate(app,db)#manager是Flask-Script的实例,这条语句在flask-Script中添加一个db命令
manager.add_command('db',MigrateCommand)#定义模型类-作者
class Author(db.Model):__tablename__ = 'author'id = db.Column(db.Integer,primary_key=True)name = db.Column(db.String(32),unique=True)email = db.Column(db.String(64))au_book = db.relationship('Book',backref='author')def __str__(self):return 'Author:%s' %self.name#定义模型类-书名
class Book(db.Model):__tablename__ = 'books'id = db.Column(db.Integer,primary_key=True)info = db.Column(db.String(32),unique=True)leader = db.Column(db.String(32))au_book = db.Column(db.Integer,db.ForeignKey('author.id'))def __str__(self):return 'Book:%s,%s'%(self.info,self.leader)if __name__ == '__main__':# 通过管理对象来启动flaskmanager.run()
进行数据库单元测试的代码 test_db.py
import unittest
from db_database import app,db,Author,Book
import timeclass TestLogin(unittest.TestCase):"""定义测试案例"""def setUp(self):"""在执行具体的测试方法前,先被调用"""# 激活测试标志app.config['TESTING'] = True# 设置用来测试的数据库,避免使用正式数据库实例[覆盖原来项目中的数据库配置]user = 'root'password = '***********'# 设置数据库,测试之前需要创建好 create database testdb charset=utf8;database = 'testdb'app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://%s:%s@127.0.0.1:3306/%s' % (user, password, database)self.app = app# 创建数据库的所有模型表:Author、Book模型表db.create_all()def tearDown(self):# 测试结束操作,删除数据库db.session.remove()db.drop_all()# 测试代码def test_append_data(self):au = Author(name='quyuan')bk = Book(info='python_book')db.session.add_all([au, bk])db.session.commit()author = Author.query.filter_by(name='quyuan').first()book = Book.query.filter_by(info='python_book').first()# 断言数据存在self.assertIsNotNone(author)self.assertIsNotNone(book)# 休眠10秒,可以到数据库中查询表进行确认time.sleep(10)if __name__ == '__main__':unittest.main()

测试执行,执行过程查看mysql的数据库表,如下:

# 切换数据库testdb
mysql> use testdb;
Database changed
mysql> 
# 查看表为空
mysql> show tables;
Empty set (0.00 sec)# 执行过程,创建表成功
mysql> show tables;
+------------------+
| Tables_in_testdb |
+------------------+
| author           |
| books            |
+------------------+
2 rows in set (0.00 sec)mysql> 
# 执行完毕,表被全部删除
mysql> show tables;
Empty set (0.00 sec)mysql> 

查看单元测试执行成功,如下:

13423234-9d930e9eea068ee7.png
13423234-0e3934319aa622f6.png

这篇关于28. Flask 使用unittest进行单元测试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1092059

相关文章

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

ModelMapper基本使用和常见场景示例详解

《ModelMapper基本使用和常见场景示例详解》ModelMapper是Java对象映射库,支持自动映射、自定义规则、集合转换及高级配置(如匹配策略、转换器),可集成SpringBoot,减少样板... 目录1. 添加依赖2. 基本用法示例:简单对象映射3. 自定义映射规则4. 集合映射5. 高级配置匹

Spring 框架之Springfox使用详解

《Spring框架之Springfox使用详解》Springfox是Spring框架的API文档工具,集成Swagger规范,自动生成文档并支持多语言/版本,模块化设计便于扩展,但存在版本兼容性、性... 目录核心功能工作原理模块化设计使用示例注意事项优缺点优点缺点总结适用场景建议总结Springfox 是

嵌入式数据库SQLite 3配置使用讲解

《嵌入式数据库SQLite3配置使用讲解》本文强调嵌入式项目中SQLite3数据库的重要性,因其零配置、轻量级、跨平台及事务处理特性,可保障数据溯源与责任明确,详细讲解安装配置、基础语法及SQLit... 目录0、惨痛教训1、SQLite3环境配置(1)、下载安装SQLite库(2)、解压下载的文件(3)、

Golang如何对cron进行二次封装实现指定时间执行定时任务

《Golang如何对cron进行二次封装实现指定时间执行定时任务》:本文主要介绍Golang如何对cron进行二次封装实现指定时间执行定时任务问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录背景cron库下载代码示例【1】结构体定义【2】定时任务开启【3】使用示例【4】控制台输出总结背景

使用Python绘制3D堆叠条形图全解析

《使用Python绘制3D堆叠条形图全解析》在数据可视化的工具箱里,3D图表总能带来眼前一亮的效果,本文就来和大家聊聊如何使用Python实现绘制3D堆叠条形图,感兴趣的小伙伴可以了解下... 目录为什么选择 3D 堆叠条形图代码实现:从数据到 3D 世界的搭建核心代码逐行解析细节优化应用场景:3D 堆叠图

Springboot如何正确使用AOP问题

《Springboot如何正确使用AOP问题》:本文主要介绍Springboot如何正确使用AOP问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录​一、AOP概念二、切点表达式​execution表达式案例三、AOP通知四、springboot中使用AOP导出

Navicat数据表的数据添加,删除及使用sql完成数据的添加过程

《Navicat数据表的数据添加,删除及使用sql完成数据的添加过程》:本文主要介绍Navicat数据表的数据添加,删除及使用sql完成数据的添加过程,具有很好的参考价值,希望对大家有所帮助,如有... 目录Navicat数据表数据添加,删除及使用sql完成数据添加选中操作的表则出现如下界面,查看左下角从左