PYTHON单元测试
PYTHON开发入门与实战11-单元测试
1. 单元测试
本章节我们来讲讲django工程中如何实现单元测试,单元测试如何编写以及在可持续项目中单元测试的重要性。
下面是单元测试的定义:
单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。
1. 它是一种验证行为
程序中的每一项功能都是测试来验证它的正确性。它为以后的开发提供支援。就算是开发后期,我们也可以轻松的增加功能或更改程序结构,而不用担心这个过程中会破坏重要的东西,它为代码的重构提供了保障。这样,我们就可以更自由的对程序进行改进。
2. 它是一种设计行为
编写单元测试将使我们从调用者观察、思考。特别是先写测试(test-first),迫使我们把程序设计成易于调用和可测试的,即迫使我们解除软件中的耦合。什么时候测试?单元测试越早越好,早到什么程度?极限编程(Extreme Programming,或简称XP)讲究TDD,即测试驱动开发,先编写测试代码,再进行开发。
不过在实际的编码过程中,我们不必过分强调先干什么后写什么,重要的是高效和个人感觉舒适。从笔者的经验来看,根据设计或需求先编写某个功能函数的框架,然后就着手编写测试函数,针对产品的功能编写测试用例,最后编写函数的实现代码,每完成一个功能点都运行单元测试,随时补充完善测试用例。这种测试同行代码编写模式,会对函数的构思有很大的帮助,如何去编写可单元测试的函数慢慢的就会变成书写和思考习惯。
所谓先编写产品功能的函数框架,是指先编写函数空的实现,考虑参数的有哪些参数和可验证的返回值,同时默认直接返回一个合适值(假定值),编译通过后即可编写测试代码,这时,函数名、参数表、返回类型都应该确定下来了,所编写的测试代码以后需修改的可能性比较小,当然由于个人编码成熟度的不同,实际开发过程中调整也在所难免,好在单元测试可以迅速跟踪调准导致的影响。
3. 它是一种编写文档的行为
单元测试是一种无价的文档,它是展示函数或类如何使用的最佳文档。这份文档是可编译、可运行的,并且它保持最新,永远与代码同步。
4. 它具有回归性。
自动化的单元测试避免了代码出现回归,编写完成之后,可以随时随地的快速运行测试。笔者的经验表明一个尽责的单元测试方法将会在软件开发的早期阶段就可以发现很多的Bug,并且修改它们的成本也很低。在软件开发的后期阶段,Bug的发现和修改将会变得更加困难,尤其修改BUG可能导致引入新的BUG,并要消耗大量的时间和开发费用。
笔者的经历的项目就遇到这样的问题,项目上线的前一天的某个BUG修改,导致当晚一直加班深夜解决新引入的BUG问题,所以后来单元测试的回归性在笔者的项目经验里最喜欢的特性。无论什么时候作出修改都要进行完整的回归测试,可以避免修改可能引入的BUG。在生命周期中尽早地对软件产品进行测试将使效率和质量得到最好的保证。在提供了经过测试的单元的情况下,系统集成过程将会极大地简化。开发人员可以将精力集中在单元之间的交互作用和全局的功能实现上,而不是陷入充满很多Bug的单元之中不能自拔。
如果考虑做一个可以持续改进和维护的项目,尤其有大量的业务规则和逻辑的系统,单元测试就显得非常重要,单元测试主要这对业务逻辑编写测试代码,确定编码是否满足测试要求。下面我们就进入Django的单元测试实践吧。
1.1. 运行单元测试
我们创建好Django app 每个app 都会创建一个单元测试tests.py的单元测试文件,代码如下:

"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
""" from django.test import TestCase class SimpleTest(TestCase): def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. """ self.assertEqual(1 + 1, 2)

我们现在可以在IDE环境中,TEST->All Tests运行这个测试例子看看单元测试运行的效果。
1.2. 开始我们的第一个单元测试
我们来看看就提交入库单这个业务来说,目前的views.py函数AddInStockBill是没办法进行单元测试的,应为期参数涉及到web请求内容参数request,如何编写可具备单元测试的功能代码也是早期编写单元测试,可以让我们逐步掌握的代码解耦的思维模式。
入库单业务的关键点,就是入库单提交后我们需要更新该入库单对应物料的库存数据,伪代码如下:
1. 根据当前入库单的物料,在库存表中查找当前物料的库存记录;
2. 如果有当前库存记录返回当前库存对象,如果没有就创建一个新的对象;
3. 更新入库单对应物料的当前库存数据;
我们来看看如何尝试测试先行的开发模式去考虑一个入库单model提交将导致库存的更新场景,用代码说话吧:

from django.test import TestCase from inventory.models import *
from inventory import views class InventoryTest(TestCase): def test_updating_inventory_in(self): #1.创建一个Item实例;
item = Item()
item.ItemId = 1
item.ItemCode = '1001'
item.ItemName = '普通螺母' #2.床建一个新入库单对象
inStockBill = InStockBill()
inStockBill.InStockBillCode='201501010001'
inStockBill.InStockDate = '2015-01-01'
inStockBill.Operator = '张三'
inStockBill.Amount = 10
inStockBill.Item = item #3.创建当前该物料的库存对象
inventory = Inventory()
inventory.InventoryId = 1
inventory.Item = item
inventory.Amount = 10 #当前库存数量 #如何构建更新库存的函数,让其可具备测试调用
views.UpdatingInventoryIn(inStockBill,inventory) #校验测试是否满足当前场景
self.assertEqual(inventory.Amount ,20)

当前我们当前运行单元测试肯定会出错,因为我们还没有编写views.UpdatingInventoryIn函数:
def UpdatingInventoryIn(inStockBill,inventory):
inventory.Amount = inventory.Amount + inStockBill.Amount
1.3. 执行单元测试
在IDE环境中执行改成我们写好的单元测试,我们看到结果如下图,测试通过。
这里我们的单元测试主要针对核心业务来构建,不考虑相关对象的获取方式,就是说测试用例是我们根据测试场景来构建的,不考虑对象是否在数据库中,也就是与持久层没有关系。早年笔者在这里也是大费周折,测试数据与持久层数据紧密耦合,结果更换数据库或者认为删除数据后,单元测试的回归测试就无法执行,单元测试的优势大打折扣。单元测试的回归性在后续代码重构,业务变更中有着巨大的优势,不能回归的单元测试价值就少了很多,所以我们在考虑单元测试时,一定要尽量与持久层数据解耦,测试用例数据在测试代码中构建。
1.4. 代码的持续改进
前面的代码中,我们的测试用例场景是假定该物料是已经有库存数据的,那如果该物料以前没有库存数据,我们的代码怎么来写呢,我们还是从测试用例开始吧,增加测试用例代码。

…
#校验测试是否满足当前场景
self.assertEqual(inventory.Amount ,20)
inventory = Inventory() #当前没有库存数据,我们创建对象属性都没有赋值
views.UpdatingInventoryIn(inStockBill,inventory)
self.assertEqual(inventory.Amount ,10)
self.assertEqual(inventory.Item.ItemId ,inStockBill.Item.ItemId)

执行测试错误,因为views.UpdatingInventoryIn(inStockBill,inventory)没有对inventory为空的情况处理,我改进代码来满足这样的需求。于是函数代码就变成了下面这样:
def UpdatingInventoryIn(inStockBill,inventory):
if (inventory.InventoryId == None):
inventory.Item = inStockBill.Item
inventory.Amount = 0
inventory.Amount = inventory.Amount + inStockBill.Amount
执行测试通过,刚才的测试用例是写在一个测试函数里还是分开写,主要是看我们的测试用例复杂度了,复杂度高的就分开来写,简单就写在一个测试函数里。函数粒度的选择由程序员来考虑了,核心就是关注可读性,函数太长我们就把函数拆小,提高可读性。(这里笔者强烈推荐《代码重构》这本很多年以前的经典书)。
最后们views里的增加入库单函数重构成如下这样代码:

@transaction.commit_on_success
def AddInStockBill(request):
if request.method == 'POST':
form = InStockBillForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
inStockBill = InStockBill()
inStockBill.InStockBillCode = cd['InStockBillCode']
inStockBill.InStockDate = cd['InStockDate']
inStockBill.Amount = cd['Amount']
inStockBill.Operator = cd['Operator']
inStockBill.Item = cd['Item']
inventorys = inStockBill.Item.inventory_set.all()
if (inventorys.count()==0):
currentInventory = Inventory()
else:
currentInventory = inventorys[0]
#注意的函数调用,你会发现更新库存与如何获取库存对象完全解耦
UpdatingInventoryIn(inStockBill,currentInventory)
currentInventory.save() #更新库存
inStockBill.save() #保存入库单数据
return HttpResponseRedirect('/success/')
else:
form = InStockBillForm()
return render_to_response('InStockAdd.html',{'form': form}
,context_instance = RequestContext(request))

1.5. 小结
如何编写单元测试代码,测试先行的模式会让编码人员去思考如何把业务逻辑抽象出来变成一个可以用单元测试来跟踪的函数单元很有帮助,如果我们在编写一个与数据库打交道的应用系统,把业务逻辑与如何获取数据解耦合对系统的可扩展性和可维护性相当的重要,尤其当我们打算构建一个可以持续改进的系统时尤为如此。
PYTHON单元测试的更多相关文章
- The Hacker's Guide To Python 单元测试
The Hacker's Guide To Python 单元测试 基本方式 python中提供了非常简单的单元测试方式,利用nose包中的nosetests命令可以实现简单的批量测试. 安装nose ...
- [译]PyUnit—Python单元测试框架(1)
1. 原文及参考资料 原文链接:http://docs.python.org/2/library/unittest.html# 参考文档: http://pyunit.sourceforge.net/ ...
- Python单元测试PyUnit框架轻度整改
原理 参考:单元测试原理 背景 年后有段时间没写代码了,所以趁着周末找了个python单元测试玩下,测试自己的Android应用.发现PyUnit虽然在单个脚本文件中添加多个测试用例,比如官网提供的方 ...
- Python单元测试框架
目录 概况 系统要求 使用PyUnit构建自己的测试 安装 测试用例介绍 创建一个简单测试用例 复用设置代码:创建固件 包含多个测试方法的测试用例类 将测试用例聚合成测试套件 嵌套测试用例 测试代码的 ...
- 一种数据与逻辑分离的Python单元测试工具
一种数据与逻辑分离的Python单元测试工具 几个概念 TestCase TestCase是一个完整的测试单元,最小的测试执行实体,就是我们常说的测试用例. TestSuite 以某种特性将测试用例组 ...
- Python单元测试框架之pytest 4 -- 断言
From: https://www.cnblogs.com/fnng/p/4774676.html Python单元测试框架之pytest -- 断言 2015-08-31 23:57 by 虫师, ...
- Python单元测试框架之pytest 3 -- fixtures
From: https://www.cnblogs.com/fnng/p/4769020.html Python单元测试框架之pytest -- fixtures 2015-08-29 13:05 b ...
- Python单元测试框架之pytest 2 -- 生成测试报告
From: https://www.cnblogs.com/fnng/p/4768239.html Python单元测试框架之pytest -- 生成测试报告 2015-08-29 00:40 by ...
- Python单元测试框架unittest使用方法讲解
这篇文章主要介绍了Python单元测试框架unittest使用方法讲解,本文讲解了unittest概述.命令行接口.测试案例自动搜索.创建测试代码.构建测试套件方法等内容,需要的朋友可以参考下 概 ...
- python单元测试框架 pyunit
概况 系统要求 使用PyUnit构建自己的测试 安装 测试用例介绍 创建一个简单测试用例 复用设置代码:创建固件 包含多个测试方法的测试用例类 将测试用例聚合成测试套件 嵌套测试用例 测试代码的放置位 ...
随机推荐
- HTML与XML总结
阅览<孙欣HTML>和<刘炜XML>过了一段时间,在这里学到的内容用思维导图来概括. HTML与XML都是标记语言. 同样点: HTML文档与XML文档有类似的结构. 前者是( ...
- 基数排序---Java实现+C++实现
基数排序是基于桶排序实现的,总之基本思想是:先基于个位进行桶排序,更新原序列:再基于十位进行桶排序,更新原序列-- code1:java import java.util.*; public clas ...
- Tuxedo入门学�
中间件介绍: 介于客户机和server之间的夹层,突破了传统的c/s架构,为构建大规模,高性能,分布式c/s应用程序提供了通信,事物,安全,容错等基础服务,屏蔽了底层应用细节,应用程序不必从底层开发, ...
- Android锁定屏幕或关闭状态-screen,高速按两次音量向下键来实现拍摄功能(1.1Framework在实现的形式层广播)
思想的实现: WindowManagerService循环读取下面的关键信息和分发形式.在PhoneWindowManager.interceptKeyBeforeQueueing方法中进行消 ...
- 国内PaaS概述和EEPlat定位
2014国内云计算产业进入快速发展阶段.热火多年来,所以云计算的云计算产业迅速进入栈桥的应用.IaaS.PaaS.SaaS各大厂商具有较强的市场布局,所以,云计算应用在这三个层次的访问,以实际使用阶段 ...
- UML学习(一)类图和对象图
对象是一个概念,一种抽象或者事物.对象能够是具有现实意义的事物,也能够是抽象的一个概念.比方,一家公司或者一个进程. 类是一组对象的集合或者抽象的概念.类具有同样的属性和方法. 介绍完基本对象和类的基 ...
- UVa 11587 - Brick Game
称号:背景:brick game有N块,给你一个整数的定数S,两个人轮流木: 的木块数是集合S中存在的随意数字.取走最后木块的人获胜.无法取则对方获胜. 题干:如今让你先取,给你一个你的结果序列串T, ...
- zabbix 实现curl 显示器
1.进入Configure->Templates 2. 新建一个模板 3.新建模板,并保存 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGFpND ...
- java流的性能优化1-文件复制
传统的I/O速度相对照较慢,它会成为系统性能的瓶颈,所以在java1.4之后提供了NIO,它是一种全新的流:它具有下面特性: 1.为全部的原是类型提供Buffer缓存支持: 2.使用java.nio. ...
- linuxc_螺纹锁紧pthread_mutex_t
在实际执行过程中的线程,我们经常需要同步多线程. 然后你可以使用互斥锁来完成任务:在使用过程中互斥锁,有pthread_mutex_init,pthread_mutex_destory,pthread ...