首先搭建单元测试框架,并完成一个最小的功能集合。
首先为我们的模型起个名字:svnauthz
。
Subversion路径授权中,用户对象(用户/别名/组)显然是最重要的基本单位,
每一条授权策略都包含一个用户对象。那么我们第一个迭代就实现用户对象:
User
类,Alias
类,
Group
类。
假设 svnauthz 的 User
,
Alias
, Group
类已经完成,
我们期望他们实现的功能是什么呢?于是在纸上写下假想任务目标(模拟python交互式命令行):
>>> from svnauthz import User, Group, Alias >>> user1=User('Tom') >>> user2=User("Jerry") >>> print user1 Tom # 显示 user1 内容(字符串化) >>> alias1=Alias('admin') >>> alias1.user = user1 >>> print alias1 admin = Tom # 显示 alias1 内容(字符串化) >>> group1 = Group('team1') >>> group2 = Group('team2') >>> group1.append(group2, user2, alias1, user1) >>> print group1 team1 = &admin, @team2, Jerry, Tom # group1 的成员列表要进行排序 >>> group2.append(group1, user1) Exception: ... # 抛出异常! group1 引起了组间的循环引用 >>> group2.append(group1, user1, autodrop=True) >>> print group2 team2 = Tom # 使用 autodrop 参数,自动抛弃冲突的组成员,而不引发异常。(即容错性)
将假想的任务目标翻译为测试用例。建立单元测试文件 test_svnauthz.py
如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- import unittest from svnauthz import * class TestStage1(unittest.TestCase): def testUser(self): user1 = User('Tom') self.assert_(str(user1) == 'Tom') def testAlias(self): user1 = User('Tom') alias1=Alias('admin') alias1.user = user1 self.assert_(str(alias1) == 'admin = Tom', str(alias1)) def testGroup(self): user1 = User('Tom') user2 = User('Jerry') alias1=Alias('admin') alias1.user = user1 group1 = Group('team1') group2 = Group('team2') group1.append(group2, user2, alias1, user1) self.assert_(str(group1) == 'team1 = &admin, @team2, Jerry, Tom') self.assertRaises(Exception, group2.append, group1, user1) group2.append(group1, user1, autodrop=True) self.assert_(str(group2) == 'team2 = Tom') if __name__ == '__main__': unittest.main()
执行测试用例:
$ python test_svnauthz.py
Traceback (most recent call last):
File "test_svnauthz.py", line 8, in <module>
from svnauthz import *
ImportError: No module named svnauthz
测试失败!不要紧,因为我们还没有写代码呢。
之前执行测试用例失败,报告:找不到 svnauthz
模组。因为模组还没有创建,当然找不到了。
于是创建一个空的模组文件 svnauthz.py
。
$ touch svnauthz.py
执行测试用例:
$ python test_svnauthz.py
EEE
======================================================================
ERROR: testAlias (__main__.TestStage1)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_svnauthz.py", line 17, in testAlias
user1 = User('Tom')
NameError: global name 'User' is not defined
...
太棒了,我们前进了一步,因为失败的原因已经不同了。错误报告说:
User
类未定义。于是我们写一些代码,
让测试用例通过。
svnauthz.py
第一个版本的代码如下:
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 """Subversion authz config file management. 5 6 Basic classes used for Subversion authz management. 7 """ 8 9 class User(object): 10 11 def __init__(self, name): 12 name = name.strip() 13 14 if not name: 15 raise Exception, 'Username is not provided' 16 17 self.__name = name 18 19 def __str__(self): 20 return self.__name
再次执行测试用例:
$ python test_svnauthz.py -v
testAlias (__main__.TestStage1) ... ERROR
testGroup (__main__.TestStage1) ... ERROR
testUser (__main__.TestStage1) ... ok
======================================================================
ERROR: testAlias (__main__.TestStage1)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_svnauthz.py", line 18, in testAlias
alias1=Alias('admin')
NameError: global name 'Alias' is not defined
...
好的,我们已经有一个测试用例(testUser
)通过了!
其他的测试用例呢?先把他们注释掉,以便提前感受一下完全通过测试的滋味。
注意:我所说的注释掉不是删除代码,也不是把每一行变为注释, 而是非常简单的将暂不考虑的测试用例改名。
将 def testAlias(self)
改为 def _testAlias(self)
将 def testGroup(self)
改为 def _testGroup(self)
注:只要不是以 |
再次执行测试用例,太棒了完全通过!
$ python test_svnauthz.py -v
testUser (__main__.TestStage1) ... ok
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
检查代码覆盖度,在 Python 下有 coverage 包可用。 用 easy_install 安装之后, 就可以使用 coverage 命令了。
$ coverage -x test_svnauthz.py
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
$ ls .coverage
.coverage
$ coverage -r -m svnauthz.py
Name Stmts Exec Cover Missing
----------------------------------------
svnauthz 8 7 87% 15
哦,看来我们离完美还是差了一点。从 coverage
的输出中可以看出,我们的测试用例并没有对 svnauthz.py
的代码测试完全:第15行没有测试到。也就是用空的用户名创建
User
对象,应该抛出异常。
我们在 testUser
用例的最后补充一条断言:
def testUser(self):
user1 = User('Tom')
self.assert_(str(user1) == 'Tom')
self.assertRaises(Exception, User, " ")
再次检查一下测试用例对代码的覆盖度。哇,100% 通过!
$ coverage -x test_svnauthz.py . ---------------------------------------------------------------------- Ran 1 test in 0.002s OK $ coverage -r -m svnauthz.py Name Stmts Exec Cover Missing ---------------------------------------- svnauthz 8 8 100%
目前来讲,代码和测试用例共存于同一个目录。我们重构一下,将模组代码放在
src
目录,将测试用例放在 tests
目录。
执行测试用例:
$ python tests/test_svnauthz.py
Traceback (most recent call last):
File "tests/test_svnauthz.py", line 8, in <module>
from svnauthz import *
ImportError: No module named svnauthz
在 test_svnauthz.py
文件头增加如下语句,
设置 Python 模组查询路径:
import sys sys.path.insert(0,'src')
测试用例又可以成功执行了。
目录 tests
下如果有多个测试用例文件,
难道要一个一个去调用么?或者用 unittest.TestSuite
去组织测试用例?其实不用这么麻烦,nosetests
可以自动发现目录下的测试用例,并执行。
鼻子测试(nosetests)是一个主动发现测试用例的 unittest 扩展。可以用 easy_install 来安装:
$ easy_install nose $ nosetests . ---------------------------------------------------------------------- Ran 1 test in 0.008s OK
代码覆盖度测试
$ nosetests --with-coverage --cover-package=svnauthz
.
Name Stmts Exec Cover Missing
----------------------------------------
svnauthz 8 8 100%
----------------------------------------------------------------------
Ran 1 test in 0.030s
OK
Copyright © 2006 WorldHello 开放文档之源 计划 |