为什么需要自动化测试
- 对于大项目,对每个单元进行测试可以快速定位错误,确保项目质量
- 在 python 中,利用 python 的测试化模块搭建一些测试不会比手 动测试一些数据更麻烦
- 在 python 交互式终端的输入输出,可以马上成为一些测试样例
- 带有和 Java 类似的单元测试框架,可以提取出不同测试用例的相 同步骤
- 回归测试:一个测试用例可以在开发过程中反复测试,保证加入新 功能后不会妨碍原有功能的运转
测试覆盖率
定义:在运行测试时运行的代码占总代码量的比例
作用:找出没有被测试过的函数和代码行
自带 trace.py:python -m trace --help
--count 统计每行被执行的次数
--trace 程序每执行一行,就将这一行打印到标准输出
统计被执行的函数名、调用关系、累积多次运行的总次数、标出没有被运行的代码行⋯⋯
Doctest
python 的 docstring 中有很多在交互式终端中运行的例子
doctest 可以检查 docstring 中样例的正确性
同样的,可以把手动测试的终端输出放进 docstring 里,变成一个 测试用例
注意:一般小型的或者简单的功能可以使用,但是较大的项目或者较复杂的功能并不推荐使用doctest
下面是一个样例
复制代码
def factorial(n):
"""Return the factorial of n, an exact integer >= 0.
>>> [factorial(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]
>>> factorial(30) 265252859812191058636308480000000
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: n must be >= 0
"""
import math
if not n >= 0:
raise ValueError("n must be >= 0")
result = 1
factor = 2
while factor <= n:
result *= factor
factor += 1
return result
if __name__ == "__main__":
import doctest
doctest.testmod()
复制代码
Unittest
- 基于 JUnit 测试框架 (JAVA)
- 比 doctest 更加灵活, 功能更加强大
- 我们要提到的django单元测试就是基于unittest的,下面先简要介绍下如何使用unittest
如何使用Unittest
先构造 unittest.TestCase 的子类
unittest.main() 用法
unittest.main() 函数将会实例化所有当前 py 文件下的 TestCase 的 子类并且执行以’test’ 开头的函数
若需要跨文件测试,则只需 import 含 testBase 子类的 py 文件再调 用 unittest.main() 即可
startUp 与 tearDown 函数
startUp 将会在每个 test 函数执行前被调用
tearDown 将会在每个 test 函数执行后被调用
通常用来初始化以及清除测试过程中的一些代码
常用的TestCase函数:
assert_(expr[, msg]) 与 failUnless(expr[, msg])
会对不正确的结果报错
assertEqual(x, y[, msg]) 与 failUnlessEqual(x, y[, msg])
值不等时报错并输出二者的值
assertAlmostEqual(x, y[, places[, msg]]) 与 failUnlessAlmostEqual(x, y[, places[, msg]])
值不近似等时报错,用于浮点数的判等
assertRaises(exc, callable, ...) 与 failUnlessRaises(exc, callable, ...)
该回调函数没有抛出 exc 的异常则报错
复制代码
下面是一个样例
class ProductTestCase(unittest.TestCase):
def testIntegers(self):
x = 3
y = 4
p = my_math.product(x, y)
self.failUnless(p == x*y, 'Integer␣multiplication␣failed')
if __name__ == '__main__': unittest.main()
*************************Wrong*************************
F
========================================================
FAIL: testIntegers (__main__.ProductTestCase)
-------------------------------------------------------
Traceback (most recent call last):
File "test_my_math.py", line 17, in testIntegers self.failUnless(p == x*y, 'Integer␣multiplication␣ failed')
AssertionError: Integer multiplication failed
-------------------------------------------------------
Ran 1 tests in 0.001s
FAILED (failures=1)
*************************Correct*************************
.
-------------------------------------------------------
Ran 1 tests in 0.001s
OK
复制代码
- 当测试结果正确时,返回一个.而不正确时是返回一个F的
- 源码检查
- PyChecker
- 检查 python 源码错误,例如传参错误,没有导入模块,同一作用域 中重定义函数、类方法等。
- 使用:pychecker [options] file1.py file2.py …
- PyLint
- 支持大部分 PyChecker 功能
- 更强大的功能,例如检查变量名是否符合规定,检查一行代码的长 度,一个声明过的接口是否被实现
- 使用:pylint [options] module_or_package
- 结合 unittest 使用
- 代码中嵌入
- PyChecker 与 PyLint 集成到 IDE
- Django单元测试
- 讲了这么多python的单元测试,那如何进行Django的单元测试呢?
- 在Django的单元测试中,仍然推荐使用python的unittest模块,像我们刚刚在上面提到的使用方法,使用类名为django.test.TestCase,继承于python的unittest.TestCase。
class TestDefault(TestCase):
def setUp(self):
# 设置配置
settings.IGNORE_WECHAT_SIGNATURE = True
# user1 => not bind
# user2 => bind
User.objects.create(open_id='abc')
User.objects.create(open_id='a', student_id='2016013265')
# textMsgs => 用户一般可能输入(成功)
self.textMsgs = ['balabala', 'gg', '抢火车票']
# 是否返回帮助
def is_default(self, content):
pattern = '对不起,没有找到您需要的信息:('
return content.find(pattern) != -1
def test_text(self):
users = User.objects.all()
for user in users:
for textMsg in self.textMsgs:
fromUser = user.open_id
curTime = str(getTimeStamp(datetime.datetime.now()))
msgId = str(random.randint(0, 99999)) + curTime
data = getTextXml(fromUser, curTime, textMsg, msgId)
response = self.client.post(
path='/wechat/',
content_type='application/xml',
data=data
)
content = str(response.content.decode('utf-8'))
self.assertEqual(self.is_default(content), True)
复制代码
执行目录下所有的测试(所有的test*.py文件):
python manage.py test
复制代码
- 运行测试的时候,测试程序会在所有以test开头的文件中查找所有的test cases(inittest.TestCase的子类),自动建立测试集然后运行测试。(注意是所有app的test文件,包括子目录中的)
执行项目的所有的test测试:
python manage.py test
复制代码
运行结果:
image.png
执行项目某个下tests包里的测试:
python manage.py xxx.tests
单独执行某个test case:
python manage.py xxx.TestDefault
单独执行某个测试方法:
python manage.py xxx.TestDefault.testxxx/py
通配测试文件名:
python manage.py test–pattern=”tests_*.py”
启用warnings提醒:
python -Wall manage.py test
复制代码
数据库
- 测试是需要数据库的,django会为测试单独生成数据库。不管你的测试是否通过,当你所有的测试都执行过后,这个测试数据库就会被销毁。
- 默认情况下,测试数据库的名字是test_DATABASE_NAME,DATABASE_NAME是你在settings.py里配置的数据库名;
- 如果你需要给测试数据库一个其他的名字,在settings.py中指定TEST_DATABASE_NAME的值
- 使用sqlite3时,数据库是在内存中创建的。
- 除了数据库是单独创建的以外,测试工具会使用相同的数据库配置–DATABASE_ENGINE, DATABASE_USER, DATABASE_HOST等等.
- 创建测试数据库的用户DATABASE_USER(settings中)指定,所以你需要确认 DATABASE_USER有足够的权限去创建数据库。
- 为了保证所有的测试都从干净的数据库开始,执行顺序如下:
1.所有的TestCase子类首先运行。
2.所有其他的单元测试(unittest.TestCase,SimpleTestCase,TransactionTestCase)。
3.其它的测试(例如doctests等)
加速测试
可以将PASSWORD_HASHERS设置为更快的算法:
PASSWORD_HASHERS = (
'django.contrib.auth.hashers.MD5PasswordHasher',
)
travis CI与django的单元测试
在.travis.yml文件中加入(修改):
script:
- python manage.py test
复制代码
参考
Beginning Python From Novice to Professional, Second Edition
blog.csdn.net/runnoob_111…
m.pythontab.com/article/843
medium.com/@MicroPyram…
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END