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构建自己的测试 安装 测试用例介绍 创建一个简单测试用例 复用设置代码:创建固件 包含多个测试方法的测试用例类 将测试用例聚合成测试套件 嵌套测试用例 测试代码的放置位 ...
随机推荐
- Maven导入eclipse缺少web-resources目录
导入其它机器的maven项目会出现缺少target/m2e/下的web-resources目录,眼下详细原因不知道, 解决方法是,直接将缺少的web-resources目录复制到项目中,错误解决
- WinForm 国际化开发一例
1.新建一个WinForm程序(在中文版的Windows下),如下,添加1个Button和1个Label 设置Form1的localizable属性为True(Form1的properties里的De ...
- Json的反序列化 .net Newtonsoft.Json
项目中有个.json文件. { "instances": [ { "name": "baidu", "url": &qu ...
- Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
之前一直用mybatis+mybatis-spring-1.1.1,系统升级mybatis使用后 mybatis-spring-1.2.2, 再其他配置均为改动的情况下执行出错: Property ' ...
- C# 6.0 (C# vNext) 的新功能:Exception-Handling Improvements
于 C# 6.0 包裹在异常处理的新功能,有两个方面的改进: 异步处理(async and await)能力 catch block 总结使用.于 C# 5.0 释放 async and await, ...
- js“分享到”侧边框伸缩实现
思路: 1,临界值是 -150 和 0 如果大于临界值,就要隐藏 2,隐藏:速度为负 显示:速度为正 3,如果与临界值相等,就清空定时器 否则,就运动 ------------------------ ...
- 对于COM对象使用ComPtr代替传统指针
对于COM对象来说使用传统指针比较麻烦,还要记得Release()防止内存泄漏,一不小心就会出现各种各样的问题.针对这种问题微软提供了对于COM对象的智能指针ComPtr,这里是官方文档https:/ ...
- Java中判断字符串是否为数字的五种方法 (转)
推荐使用第二个方法,速度最快. 方法一:用JAVA自带的函数 public static boolean isNumeric(String str){ for (int i = str.length( ...
- Javascript作用域问题的构造函数的变量
构造函数new对于使用.代表创建对象.此外,它可以被用作普通的函数调用,因为它也是一个功能. function Person(name) { this.name=name; } Person(12); ...
- OCP-1Z0-051-名称解析-文章12称号
12. You need to produce a report where each customer's credit limit has been incremented by $1000. I ...