大家好,我系渣渣辉, 给大家推荐一款超好玩的游戏——贪玩改错。
【一】大怪出现 (异常处理)
一提到怪物,人们就会想到bug,可是这只是其中一类。其实还有两类:一个是用户输入错误,我们会提醒用户让他们检查;另一类就是完全无法预测的错误了,像python这种高级语言都贴心地内置了一套异常处理机制,比如:
看一下可能出现小怪的模样:
所以啊,世上虽然有像我这么完美的玩家,却没有一个顺畅的成功之路。游戏能一次通关并正常运行的概率微乎其微。但我渣渣辉岂能败给这些小小的错误?!于是我集齐了一整套神器来修复bug,开始了打怪调试之路(就是我再写一段代码去检查错误)……
【二】打怪从小开始(单元调试)
一屋不扫何以扫天下?打怪也要从小抓起、从一个模块、一个函数或一个类开始进行!
比如函数abs()吧,我们就写出几个测试用的例子:输入正数,期待返回值与输入相同;输入负数,期待返回值与输入相反;输入0,期待返回0;输入非数值类型,比如None、[]、{},期待抛出TypeError。 把上面的测试用例放到一个测试模块里,就是一个完整的单元测试。那我用什么方法呢?
(1)Python单元测试必杀技之unittest
了解xUnit系列框架的小伙伴对这个肯定很熟悉,因为这就是其中一员。(可惜我没学过呜呜!
命令行接口、测试案例自动搜索、创建测试代码、构建测试套件方法等内容,需要的朋友可以参考下
①它主要包括:
1.测试脚手架(test fixture)——准备工作和扫尾工作.包括setUp()和tearDown().
2.测试案例(test case)——最小的测试单元.
3.测试套件(test suite)——测试案例的集合.
4.测试运行器(test runner)——测试执行的组件.
②自动击杀(测试案例自动搜索)
unittest支持简单的test discovery. 命令行传入discovery后,框架会自动在当前目录搜索要测试的案例并执行.搜索目录必须是包或者模块.基本使用如下:
cd project_directory
python -m unittest discover
技能选择如下:
-v, –verbose 输出信息的详细级别;
-s, –start-directory directory 开始搜索目录 (默认为当前目录);
-p, –pattern pattern 匹配的文件名 (默认为test*.py);
-t, –top-level-directory directory 搜索的顶层目录 (默认为start directory)。
③创建测试代码
方式一:创建子类继承unittest.TestCase
class WidgestTestCase(unittest.TestCase):
def setup(self):
pass
def runTest(self):
pass
def tearDown(self):
pass
方式二:编写以test开头的方法
class WidgetTestCase(unittest.TestCase):
def setUp(self):
pass
def test_xx1(self)
def test_xx2(self)
...
def test_xxN(self)
def tearDown(self):
pass
④构建测试套件
def suite():
suite = unittest.TestSuite()
suite.addTest(WidgetTestCase('test_default_size'))
suite.addTest(WidgetTestCase('test_resize'))
return suite
或
def suite():
tests = ['test_default_size', 'test_resize']
return unittest.TestSuite(map(WidgetTestCase, tests))
(2) Python单元测试必杀技之pytest
这个必杀技与python自带的unittest类似,但是比unittest使用起来更简单,打怪效率也更高。而且它能够支持简单的单元测试和复杂的功能测试;支持参数化;具有很多第三方插件,并且可以自定义扩展;方便的和持续集成工具集成。
听起来就好牛逼的样子是不是?实际上也真的很牛逼!
①安装
像安装python的其它软件一样,直接用pip安装。
pip install -U pytest
py.test --version # 安装完成后,验证安装的版本
②实战
# content of test_sample.py
def func(x): # 定义一个被测试函数func
return x+1 # 该函数将传递进来的参数加1后返回
def test_func(): # 再定义一个测试函数test_func用来对func进行测试
assert func(3) == 5 #用断言语句assert来对结果进行验证
当打多个怪(需要编写多个测试样例)时,我们可以将其放到一个测试类当中:
# content of test_class.py
class TestClass:
def test_one(self):
x = "this"
assert 'h' in x
def test_two(self):
x = "hello"
assert hasattr(x, 'check')
③如何召唤技能(如何编写pytest测试样例)
通过上面2个实例,我们发现编写pytest测试样例非常简单,只需要按照下面的规则:
测试文件以test_开头(以_test结尾也可以);测试类以Test开头,并且不能带有 __init__ 方法;测试函数以test_开头;断言使用基本的assert即可。
④如何使用技能(如何执行pytest测试样例)
方法很多种,上面第一个实例是直接执行py.test,第二个实例是传递了测试文件给py.test。其实py.test有好多种方法执行测试:
1. py.test # run all tests below current dir
2. py.test test_mod.py # run tests in module
3. py.test somepath # run all tests below somepath
4. py.test -k stringexpr # only run tests with names that match the
# the "string expression", e.g. "MyClass and not method"
# will select TestMyClass.test_something
# but not TestMyClass.test_method_simple
8. py.test test_mod.py::test_func # only run tests that match the "node ID", # e.g "test_mod.py::test_func" will select # only test_func in test_mod.py
⑤所获成就(测试报告)
pytest可以方便的生成测试报告,即可以生成HTML的测试报告,也可以生成XML格式的测试报告用来与持续集成工具集成。
生成HTML格式报告: py.test --resultlog=path
生成XML格式的报告: py.test --junitxml=path
⑥游戏提示(获取帮助信息)
1. py.test --version # shows where pytest was imported from
2. py.test --fixtures # show available builtin function arguments
3. py.test -h | --help # show help on command line and config file options
⑦最佳作战环境
其实对于测试而言,特别是在持续集成环境中,我们的所有测试最好是在虚拟环境中。这样不同的虚拟环境中的测试不会相互干扰的。由于我们的实际工作中,在同一个Jekins中,运行了好多种不同项目册的测试,因此,各个测试项目运行在各自的虚拟环境中。
将pytest安装在虚拟环境中:
1、将当前目录创建为虚拟环境virtualenv . # create a virtualenv directory in the current directory source bin/activate # on unix
2、在虚拟环境中安装pytest:
pip install pytest �
【三】全杀(调试)
(1)技能一:print
这一技能简单直接粗暴有效,就是正面刚,不服就把你打……印出来:
# error.py
def foo(s):
n = int(s)
print '>>> n = %d' % n
return 10 / n
def main():
foo('0')
main()
执行后在输出中查找打印的变量值就行。
由于这一技能过于残暴,所以用过场面一度混乱,会产生很多垃圾信息。
(2) 技能二:assert
这一技能不仅可替代print(凡是用print来辅助查看的地方,都可以用断言(assert)来替代),而且不会场面会整齐一些(因为启动Python解释器时可以用-O参数来关闭assert,关闭后,所有的assert语句都当成pass来看)。
# error.py
def foo(s):
n = int(s)
assert n != 0, 'n is zero!' # 表达式n != 0应该是True,否则后面的代码就会出错。
return 10 / n
def main():
foo('0') # 如果断言失败,assert语句本身就会抛出AssertionError
(3) 技能三:logging(必杀技!!)
这一绝妙技能不仅不会抛出错误,而且可以输出到文件:
# error.py
import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
print 10 / n
logging允许你指定记录信息的级别,有debug,info,warning,error等几个级别,当我们指定level=INFO时,logging.debug就不起作用了。同理,指定level=WARNING后,debug和info就不起作用了。这样一来,你可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。
logging的另一个迷人之处是通过简单的配置,一条语句可以同时输出到不同的地方,比如console和文件。
(4) 技能四:pbd
启动Python的调试器pdb,让程序以单步方式运行,可以随时查看运行状态。然而,这种通过pdb在命令行调试的方法理论上是万能的,但实在是太麻烦了,如果有一千行代码,要运行到第999行得敲多少命令啊。还好,我们还有另一种调试方法——pdb.set_trace()
这个方法也是用pdb,但是不需要单步执行,我们只需要import pdb,然后,在可能出错的地方放一个pdb.set_trace(),就可以设置一个断点:
# error.py
import pdb
s = '0'
n = int(s)
pdb.set_trace()
# 运行到这里会暂停并进入pdb调试环境,可以用命令p查看变量,或用命令c继续运行
print 10 / n
(5) 技能五:IDE
如果要比较爽地设置断点、单步执行,就需要一个支持调试功能的IDE。目前比较好的Python IDE有PyCharm。另外,eclipse加上pvdev插件也可以调试Python程序。
探挽改错,介四里没有挽过的船新版本,挤需体验三番钟,里造会干我一样,爱像借款游戏。