TDD in Expert Python Programmin
Test-Driven Development Principles
TDD consists of writing test cases that cover a desired feature, then writing the feature itself. In other words, the usage examples are written before the code even exists.
For example, a developer who is asked to write a function that provides the average value of a sequence of numbers will first write a few examples on how to use it, and
the expected results:
assert average(1, 2, 3) == 2
assert average(1, -3) == -1
These examples can be provided by another person as well. From there, the function can be implemented until the two examples work:
>>> def average(*numbers):
... return sum(numbers) / len(numbers)
...
>>> assert average(1, 2, 3) == 2
>>> assert average(1, -3) == -1
A bug or an unexpected result is a new example of usage the function should be able to deal with:
>>> assert average(0, 1) == 0.5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
The code can be changed accordingly, until the new test passes:
>>> def average(*numbers):
... # makes sure all numbers can be used as floats
... numbers = [float(number) for number in numbers]
... return sum(numbers) / float(len(numbers))
...
>>> assert average(0, 1) == 0.5
And more cases will make the code evolve:
>>> try:
... average()
... except TypeError:
... # we want an empty sequence to throw a type error
... pass
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 3, in average
ZeroDivisionError: integer division or modulo by zero
>>>
>>> def average(*numbers):
... if numbers == ():
... raise TypeError(('You need to provide at '
... 'least one number'))
... numbers = [float(number) for number in numbers]
... return sum(numbers) / len(numbers)
...
>>> try:
... average()
... except TypeError:
... pass
...
From there all tests can be gathered in a test function, which is run
every time the code evolves:
>>> def test_average():
... assert average(1, 2, 3) == 2
... assert average(1, -3) == -1
... assert average(0, 1) == 0.5
... try:
... average()
... except TypeError:
... pass
...
>>> test_average()
Every time a change is made, test_average is changed together with average,then run again to make sure all cases still work. The usage is to gather all tests in the tests folder of the current package. Each module can have a corresponding test module there.This approach provides a lot of benefits by:
• Preventing software regression
• Improving code quality
• Providing the best low-level documentation
• Producing robust code faster
Preventing Software Regression
We all face software regression issues in our developer lives. Software regression is a new bug introduced by a change. Regressions happen because of the simple fact that it is impossible at some point to guess what a single change in a codebase might lead to. Changing some code might break some other features, and sometimes lead to vicious side effects, such as silently corrupting data.
To avoid regression, the whole set of features software provides should be tested every time a change occurs.
Opening a codebase to several developers amplifies the problem, since each person will not be fully aware of all development activities. While having a version control system prevents conflicts, it does not prevent all unwanted interactions.
TDD helps reduce software regression. The whole software can be automatically tested after each change. This will work as long as each feature has the proper set of
tests. When TDD is properly done, the test base grows together with the codebase. Since a full test campaign can last for quite a long time, it is a good practice to delegate it to a buildbot, which can do the work in the background (this is described in Chapter 8). But local re-launching of the tests should be done manually by the user, at least for the concerned modules.
Improving Code Quality
When a new module, class, or a function is written, a developer focuses on how to write it and how to produce the best piece of code he or she can. But while he or she
is concentrating on algorithms, he or she might lose the user's point of view: How and when will his or her function be used? Are the arguments easy and logical to use? Is the name of the API right?
This is done by applying the tips described in the previous chapters, such as Choosing Good Names. But the only way to do it efficiently is to write usage examples. This is when the developer realizes if the code he or she wrote is logical and easy to use. Often, the first refactoring occurs right after the module, class, or function is finished.
Writing tests, which are use cases for the code, helps in having this user point of view. Developers will, therefore, often produce a better code when they use TDD. It is difficult to test gigantic functions that both calculate things as well as have side effects. Code that is written with testing in mind tends to be architected more cleanly
and modularly.
Providing the Best Developer Documentation
Tests are the best place for a developer to learn how software works. They are the use cases the code was primarily created for. Reading them provides a quick and deep
insight into how the code works. Sometimes, an example is worth a thousand words. The fact that these tests are always up to date with the codebase makes them the best
developer documentation a piece of software can have. Tests don't go stale in the same way documentation does, otherwise they would fail.
Producing Robust Code Faster
Writing without tests leads to extensive debugging sessions. A bug in one part of the software might be felt in a distant part of that software. Since you don't know who to
blame, you spend an inordinate amount of time debugging. It's better to fight small bugs one at a time when a test fails, because you'll have a better clue as to where the
real problem is. And testing is often more fun that debugging because it is coding.
If you measure the time taken to fix the code together with the time taken to write it, it will usually be longer than the time a TDD approach would take. This is not obvious when you start a new piece of code. This is because the time taken to set up test environment and write the first few tests is extremely long compared to the time taken just to write the first pieces of code.
But there are some test environments that are really hard to set up. For instance, when your code interacts with an LDAP or an SQL server, writing tests is not obvious at all. This is covered in the Fakes and Mocks section in this chapter.
TDD in Expert Python Programmin的更多相关文章
- Expert Python programming - Reading Notes
1. MRO: method resolution order lookup order: L(MyClass) = [MyClass, merged(L(Base1), L(Base2), Base ...
- python BDD&TDD
教程一:行为驱动开发(BDD) 基于Python的行为驱动开发实战: http://python.jobbole.com/81303/ 基于Python的行为驱动开发实战 英语原文地址: http:/ ...
- [Python] 学习资料汇总
Python是一种面向对象的解释性的计算机程序设计语言,也是一种功能强大且完善的通用型语言,已经有十多年的发展历史,成熟且稳定.Python 具有脚本语言中最丰富和强大的类库,足以支持绝大多数日常应用 ...
- Python学习资料下载地址(转)
[转]Python学习资料和教程pdf 开发工具: Python语言集成开发环境 Wingware WingIDE Professional v3.2.12 Python语言集成开发环境 Wingwa ...
- [转]Python学习资料和教程pdf
开发工具: Python语言集成开发环境 Wingware WingIDE Professional v3.2.12 Python语言集成开发环境 Wingware WingIDE Professio ...
- 原创翻译-测试驱动开发(TDD)
测试驱动开发原则 翻译自<<Expert Python Programming>> 测试驱动开发是指首先编写包含所有测试软件特点的测试集,然后再去开发软件.也就是说,在编写软件 ...
- 关于Python编程的一些问答
关于Python编程的一些问答 导语 大约1个月前,oschina.net和华章图书一起合作做了一个活动:OSC第51期高手问答--聊聊python那些事,来推广我参与撰写的书<编写高质量代码: ...
- 转:Python语言编程学习资料(电子书+视频教程)下载汇总
开发工具: Python语言集成开发环境 Wingware WingIDE Professional v3.2.12 Python语言集成开发环境 Wingware WingIDE Professio ...
- 关于Python Package下的Module import方式[转]
2012年有一个目标我没有达成,那就是深入学习和使用Python语言.这个目标被其他学习任务和工作无情的抢占了,当然最主要的原因还是我重视不够^_^. 近期恰逢有一些Python工程的开发工作要做,就 ...
随机推荐
- Mybatis Physical Pagination
1. Requirements: when we use the sql like "select * from targetTable", we get all records ...
- mybatis中自建的类型别名
在使用mybatis过程中经常用到类型别名,除了我们自己新建的别名外,mybatis还自带了很多类型别名和java中的类型的映射,下面先看一个自建的别名的配置 <typeAliases> ...
- 基于java的socket编程
#开头的废话#学习java已经半个月了,原本在抠教材里面的字眼时,觉得教材好厚,要看完不知道要到猴年马月去了.突然在网上看到一个教程,里面老师说学编程语言书不用太细看,看个大概,知道里面讲些什么就好, ...
- [Xamarin.Android] ActionBar Tips
[Xamarin.Android] ActionBar Tips ActionBar用途 快速搞懂 ActionBar的用途,可以参考下列文章: [Android]使用 ActionBarCompat ...
- 使django与数据库保持长连接
最近遇到一个很蛋疼的问题,写了一个后台管理系统, 由于是后台管理系统,所以使用频率不是很高,当django程序在闲置一段时间后,再次打开后台系统,就变得很慢,然后又好了.查了很多方面,从模板引擎到请求 ...
- C# 如何使用 svcutil.exe 创建 WCF 客户端代码
工具:svcutil.exe 参数:指定wsdl.输出源码文件.输出配置文件 示例: D:\>svcutil.exe http://localhost:8087/DataService/?wsd ...
- Android 在外部存储读写文件
本文主要介绍android中如何在外部存储读写数据 sd卡的路径 sdcard:2.3之前的sd卡路径 mnt/sdcard:4.3之前的sd卡路径 storage/sdcard:4.3之后的sd卡路 ...
- Lucene总体架构
Lucene总的来说是:• 一个高效的,可扩展的,全文检索库.• 全部用Java实现,无须配置.• 仅支持纯文本文件的索引(Indexing)和搜索(Search).• 不负责由其他格式的文件抽取纯文 ...
- IOS单例
单例就是只有一个实例. 两种常见的创建方法: 1. : static A *a = nil; + (A *)shareInstance { if (!a) a = [[self alloc] init ...
- 【读书笔记】iOS-垃圾回收
Objective-C的垃圾回收器是一种继承性的垃圾回收器.与那些已经存在了一段时间的对象相比,新创建的对象更可能被当成垃圾.垃圾回收器定期检查变量和对象以及它们之间的指针,当发现没有任何变量指向某个 ...