第十六章 测试基础
调试是程序员躲不开的宿命,是编程工作的有机组成部分。 本章介绍测试的基本知识。培养如何养成在编程中进行测试的习惯,并介绍一些可帮 助编写测试的工具。
16.1 先测试再编码
要避免代码在开发途中被淘汰,必须能够应对变化并具备一定的灵活性,因此为程序的各个 部分编写测试至关重要(这称为单元测试),而且是应用程序设计工作的重要组成部分。极限编 程先锋引入了“测试一点点,再编写一点点代码”的理念。换而言之,测试在先,编码在后。这也称为测试驱动的编程。
16.1.1 准确的需求说明
开发软件时,必须先知道软件要解决什么问题——要实现什么样的目标。要阐明程序的目标,可编写需求说明,也就是描述程序必须满足何种需求的文档(或便条)。 测试程序可以包含在程序内如:
#计算矩形面积
def rect_area(heigth,width): #程序中被调用的函数
return heigth*heigth #函数中有一处错误
height = 3
width = 4
correct_answer = 12 #设定运行结果
answer = rect_area(height, width)
if answer == correct_answer: #对运行结果进行判断
print('程序测试通过 ')
else:
print('程序中存在错误!')
运行结果:
C:\Users\zhx\AppData\Local\Programs\Python\Python37\python.exe E:/pythonProjects/twisted/finger.py
程序中存在错误!
Process finished with exit code 0
16.1.2 做好应对变化的准备
自动化测试不仅可在你编写程序时提供极大的帮助,还有助于在你修改代码时避免累积错 误,这在程序规模很大时尤其重要。
16.1.3 测试四步曲
(1) 确定需要实现的新功能。可将其记录下来,再为之编写一个测试。
(2) 编写实现功能的框架代码,让程序能够运行(不存在语法错误之类的问题),但测试依然 无法通过。测试失败是很重要的,因为这样你才能确定它可能失败。 不断重复这 个过程:确定测试失败后,再试图让它成功。
(3) 编写让测试刚好能够通过的代码。
(4) 改进(重构)代码以全面而准确地实现所需的功能,同时确保测试依然能够成功。
16.2 测试工具
有两个杰出的模块可替你自动完成测试过程。
一是unittest:一个通用的测试框架。
一是 doctest:一个更简单的模块,是为检查文档而设计的,但也非常适合用来编写单元测试。
16.2.1 doctest
使用doctest非常方便,只要在模块最后添加如下代码即可:
#... ...以上是模块主体
if __name__=='__main__':
import doctest,模块名
doctest.testmod(模块名)
16.2.2 unittest
虽然doctest使用起来很容易,但unittest(基于流行的Java测试框架JUnit)更灵活、更强大。
unittest的使用和doctest类似:
import json
import unittest
from area import area
f = open('illinois.json', 'r')
illinois = json.loads(f.read())
world = {
'type': 'Polygon',
'coordinates': [
[
[-180, -90],
[-180, 90],
[180, 90],
[180, -90],
[-180, -90]
]
]
}
illinois_area = 145978332359.36746
world_area = 511207893395811.06
class AreaTestCase(unittest.TestCase):
def test_area_illinois_with_string(self):
""" illinois.json文件输入计算机 """
# 回到文件的顶端
f.seek(0)
illinois = f.read()
self.assertEqual(round(area(illinois), 2), round(illinois_area, 2))
def test_area_illinois(self):
""" Compute the area of illinois """
self.assertEqual(round(area(illinois), 2), round(illinois_area, 2))
def test_area_world(self):
""" Compute the area of the whole world """
self.assertEqual(area(world), world_area)
def test_point_area(self):
""" Compute the area of a point """
self.assertEqual(area({'type': 'Point', 'coordinates': [0, 0]}), 0)
def test_liststring_area(self):
""" Compute the area of a line string """
self.assertEqual(area({'type': 'LineString', 'coordinates': [[0, 0], [1, 1]]}), 0)
def test_geometry_collection_area(self):
""" Compute the area of a geometry collection """
total = illinois_area + world_area
self.assertEqual(area({'type': 'GeometryCollection', 'geometries': [world, illinois]}), total)
if __name__ == '__main__':
unittest.main()
函数unittest.main负责替你运行测试:实例化所有的TestCase子类,并运行所有名称以test 打头的方法 。
运行结果:
C:\Users\xx\AppData\Local\Programs\Python\Python37\python.exe E:/pythonProjects/twisted/finger.py
......
----------------------------------------------------------------------
Ran 6 tests in 0.004s
OK
Process finished with exit code 0
其中......代表六个函数通过测试,如果不能通过测试会显示F。
附 illinois.json文件:
{
"type": "MultiPolygon",
"coordinates": [[
[
[
-88.071564,
37.51099
],
[
-88.087883,
37.476273
],
[
-88.311707,
37.442852
],
[
-88.359177,
37.409309
],
[
-88.419853,
37.420292
],
[
-88.467644,
37.400757
],
[
-88.511322,
37.296852
],
[
-88.501427,
37.257782
],
[
-88.450699,
37.205669
],
[
-88.422516,
37.15691
],
[
-88.45047,
37.098671
],
[
-88.476799,
37.072144
],
[
-88.4907,
37.06818
],
[
-88.517273,
37.06477
],
[
-88.559273,
37.072815
],
[
-88.61422,
37.109047
],
[
-88.68837,
37.13541
],
[
-88.739113,
37.141182
],
[
-88.746506,
37.152107
],
[
-88.863289,
37.202194
],
[
-88.932503,
37.218407
],
[
-88.993172,
37.220036
],
[
-89.065033,
37.18586
],
[
-89.116821,
37.112137
],
[
-89.146347,
37.093185
],
[
-89.169548,
37.064236
],
[
-89.174332,
37.025711
],
[
-89.150246,
36.99844
],
[
-89.12986,
36.988113
],
[
-89.193512,
36.986771
],
[
-89.210052,
37.028973
],
[
-89.237679,
37.041733
],
[
-89.264053,
37.087124
],
[
-89.284233,
37.091244
],
[
-89.303291,
37.085384
],
[
-89.3097,
37.060909
],
[
-89.264244,
37.027733
],
[
-89.262001,
37.008686
],
[
-89.282768,
36.999207
],
[
-89.310982,
37.009682
],
[
-89.38295,
37.049213
],
[
-89.37999,
37.099083
],
[
-89.423798,
37.137203
],
[
-89.440521,
37.165318
],
[
-89.468216,
37.224266
],
[
-89.465309,
37.253731
],
[
-89.489594,
37.256001
],
[
-89.513885,
37.276402
],
[
-89.513885,
37.304962
],
[
-89.50058,
37.329441
],
[
-89.468742,
37.339409
],
[
-89.435738,
37.355717
],
[
-89.427574,
37.411018
],
[
-89.453621,
37.453186
],
[
-89.494781,
37.491726
],
[
-89.524971,
37.571957
],
[
-89.513367,
37.615929
],
[
-89.51918,
37.650375
],
[
-89.513374,
37.67984
],
[
-89.521523,
37.694798
],
[
-89.581436,
37.706104
],
[
-89.666458,
37.745453
],
[
-89.675858,
37.78397
],
[
-89.691055,
37.804794
],
[
-89.728447,
37.840992
],
[
-89.851715,
37.905064
],
[
-89.861046,
37.905487
],
[
-89.866814,
37.891876
],
[
-89.900551,
37.875904
],
[
-89.937874,
37.878044
],
[
-89.978912,
37.911884
],
[
-89.958229,
37.963634
],
[
-90.010811,
37.969318
],
[
-90.041924,
37.993206
],
[
-90.119339,
38.032272
],
[
-90.134712,
38.053951
],
[
-90.207527,
38.088905
],
[
-90.254059,
38.122169
],
[
-90.289635,
38.166817
],
[
-90.336716,
38.188713
],
[
-90.364769,
38.234299
],
[
-90.369347,
38.323559
],
[
-90.358688,
38.36533
],
[
-90.339607,
38.390846
],
[
-90.301842,
38.427357
],
[
-90.265785,
38.518688
],
[
-90.26123,
38.532768
],
[
-90.240944,
38.562805
],
[
-90.183708,
38.610271
],
[
-90.183578,
38.658772
],
[
-90.20224,
38.700363
],
[
-90.196571,
38.723965
],
[
-90.163399,
38.773098
],
[
-90.135178,
38.785484
],
[
-90.121727,
38.80051
],
[
-90.113121,
38.830467
],
[
-90.132812,
38.853031
],
[
-90.243927,
38.914509
],
[
-90.278931,
38.924717
],
[
-90.31974,
38.924908
],
[
-90.413071,
38.96233
],
[
-90.469841,
38.959179
],
[
-90.530426,
38.891609
],
[
-90.570328,
38.871326
],
[
-90.627213,
38.880795
],
[
-90.668877,
38.935253
],
[
-90.70607,
39.037792
],
[
-90.707588,
39.058178
],
[
-90.690399,
39.0937
],
[
-90.716736,
39.144211
],
[
-90.718193,
39.195873
],
[
-90.732338,
39.224747
],
[
-90.738083,
39.24781
],
[
-90.779343,
39.296803
],
[
-90.850494,
39.350452
],
[
-90.947891,
39.400585
],
[
-91.036339,
39.444412
],
[
-91.064384,
39.473984
],
[
-91.093613,
39.528927
],
[
-91.156189,
39.552593
],
[
-91.203247,
39.600021
],
[
-91.317665,
39.685917
],
[
-91.367088,
39.72464
],
[
-91.373421,
39.761272
],
[
-91.381714,
39.803772
],
[
-91.449188,
39.863049
],
[
-91.450989,
39.885242
],
[
-91.434052,
39.901829
],
[
-91.430389,
39.921837
],
[
-91.447243,
39.946064
],
[
-91.487289,
40.005753
],
[
-91.504005,
40.066711
],
[
-91.516129,
40.134544
],
[
-91.506546,
40.200459
],
[
-91.498932,
40.251377
],
[
-91.486694,
40.309624
],
[
-91.448593,
40.371902
],
[
-91.418816,
40.386875
],
[
-91.385757,
40.392361
],
[
-91.372757,
40.402988
],
[
-91.385399,
40.44725
],
[
-91.374794,
40.503654
],
[
-91.382103,
40.528496
],
[
-91.412872,
40.547993
],
[
-91.411118,
40.572971
],
[
-91.37561,
40.603439
],
[
-91.262062,
40.639545
],
[
-91.214912,
40.643818
],
[
-91.162498,
40.656311
],
[
-91.129158,
40.682148
],
[
-91.119987,
40.705402
],
[
-91.092751,
40.761547
],
[
-91.088905,
40.833729
],
[
-91.04921,
40.879585
],
[
-90.983276,
40.923927
],
[
-90.960709,
40.950504
],
[
-90.954651,
41.070362
],
[
-90.957787,
41.104359
],
[
-90.990341,
41.144371
],
[
-91.018257,
41.165825
],
[
-91.05632,
41.176258
],
[
-91.101524,
41.231522
],
[
-91.102348,
41.267818
],
[
-91.07328,
41.334896
],
[
-91.055786,
41.401379
],
[
-91.027489,
41.423508
],
[
-91.000694,
41.431084
],
[
-90.949654,
41.421234
],
[
-90.844139,
41.444622
],
[
-90.7799,
41.449821
],
[
-90.708214,
41.450062
],
[
-90.658791,
41.462318
],
[
-90.6007,
41.509586
],
[
-90.54084,
41.52597
],
[
-90.454994,
41.527546
],
[
-90.434967,
41.543579
],
[
-90.423004,
41.567272
],
[
-90.348366,
41.586849
],
[
-90.339348,
41.602798
],
[
-90.341133,
41.64909
],
[
-90.326027,
41.722736
],
[
-90.304886,
41.756466
],
[
-90.25531,
41.781738
],
[
-90.195839,
41.806137
],
[
-90.154518,
41.930775
],
[
-90.14267,
41.983963
],
[
-90.150536,
42.033428
],
[
-90.168098,
42.061043
],
[
-90.166649,
42.103745
],
[
-90.176086,
42.120502
],
[
-90.191574,
42.122688
],
[
-90.230934,
42.159721
],
[
-90.323601,
42.197319
],
[
-90.367729,
42.210209
],
[
-90.407173,
42.242645
],
[
-90.417984,
42.263924
],
[
-90.427681,
42.340633
],
[
-90.441597,
42.360073
],
[
-90.491043,
42.388783
],
[
-90.563583,
42.421837
],
[
-90.605827,
42.46056
],
[
-90.648346,
42.475643
],
[
-90.651772,
42.494698
],
[
-90.638329,
42.509361
],
[
-90.419975,
42.508362
],
[
-89.923569,
42.504108
],
[
-89.834618,
42.50346
],
[
-89.400497,
42.49749
],
[
-89.359444,
42.497906
],
[
-88.939079,
42.490864
],
[
-88.764954,
42.490906
],
[
-88.70652,
42.489655
],
[
-88.297897,
42.49197
],
[
-88.194702,
42.489613
],
[
-87.79731,
42.489132
],
[
-87.836945,
42.314213
],
[
-87.760239,
42.156456
],
[
-87.670547,
42.059822
],
[
-87.612625,
41.847332
],
[
-87.529861,
41.723591
],
[
-87.532646,
41.469715
],
[
-87.532448,
41.301304
],
[
-87.531731,
41.173756
],
[
-87.532021,
41.00993
],
[
-87.532669,
40.745411
],
[
-87.53717,
40.49461
],
[
-87.535675,
40.483246
],
[
-87.535339,
40.166195
],
[
-87.535774,
39.887302
],
[
-87.535576,
39.609341
],
[
-87.538567,
39.477448
],
[
-87.540215,
39.350525
],
[
-87.597664,
39.338268
],
[
-87.625237,
39.307404
],
[
-87.610619,
39.297661
],
[
-87.615799,
39.281418
],
[
-87.606895,
39.258163
],
[
-87.584564,
39.248753
],
[
-87.588593,
39.208466
],
[
-87.594208,
39.198128
],
[
-87.607925,
39.196068
],
[
-87.644257,
39.168507
],
[
-87.670326,
39.146679
],
[
-87.659454,
39.130653
],
[
-87.662262,
39.113468
],
[
-87.631668,
39.103943
],
[
-87.630867,
39.088974
],
[
-87.612007,
39.084606
],
[
-87.58532,
39.062435
],
[
-87.581749,
38.995743
],
[
-87.591858,
38.994083
],
[
-87.547905,
38.977077
],
[
-87.53347,
38.963703
],
[
-87.530182,
38.931919
],
[
-87.5392,
38.904861
],
[
-87.559059,
38.869812
],
[
-87.550507,
38.857891
],
[
-87.507889,
38.795559
],
[
-87.519028,
38.776699
],
[
-87.508003,
38.769722
],
[
-87.508316,
38.736633
],
[
-87.543892,
38.685974
],
[
-87.588478,
38.672169
],
[
-87.625191,
38.642811
],
[
-87.628647,
38.622917
],
[
-87.619827,
38.599209
],
[
-87.640594,
38.593178
],
[
-87.652855,
38.573872
],
[
-87.672943,
38.547424
],
[
-87.65139,
38.515369
],
[
-87.653534,
38.500443
],
[
-87.679909,
38.504005
],
[
-87.692818,
38.481533
],
[
-87.756096,
38.466125
],
[
-87.758659,
38.457096
],
[
-87.738953,
38.44548
],
[
-87.748428,
38.417965
],
[
-87.784019,
38.378124
],
[
-87.834503,
38.352524
],
[
-87.850082,
38.286098
],
[
-87.863007,
38.285362
],
[
-87.874039,
38.316788
],
[
-87.883446,
38.315552
],
[
-87.888466,
38.300659
],
[
-87.914108,
38.281048
],
[
-87.913651,
38.302345
],
[
-87.925919,
38.304771
],
[
-87.980019,
38.241085
],
[
-87.986008,
38.234814
],
[
-87.977928,
38.200714
],
[
-87.932289,
38.171131
],
[
-87.931992,
38.157528
],
[
-87.950569,
38.136913
],
[
-87.973503,
38.13176
],
[
-88.018547,
38.103302
],
[
-88.012329,
38.092346
],
[
-87.964867,
38.096748
],
[
-87.975296,
38.073307
],
[
-88.034729,
38.054085
],
[
-88.043091,
38.04512
],
[
-88.041473,
38.038303
],
[
-88.021698,
38.033531
],
[
-88.029213,
38.008236
],
[
-88.021706,
37.975056
],
[
-88.042511,
37.956264
],
[
-88.041771,
37.934498
],
[
-88.064621,
37.929783
],
[
-88.078941,
37.944
],
[
-88.084,
37.92366
],
[
-88.030441,
37.917591
],
[
-88.026588,
37.905758
],
[
-88.044868,
37.896004
],
[
-88.100082,
37.90617
],
[
-88.101456,
37.895306
],
[
-88.075737,
37.867809
],
[
-88.034241,
37.843746
],
[
-88.042137,
37.827522
],
[
-88.089264,
37.831249
],
[
-88.086029,
37.817612
],
[
-88.035576,
37.805683
],
[
-88.072472,
37.735401
],
[
-88.133636,
37.700745
],
[
-88.15937,
37.660686
],
[
-88.157631,
37.628479
],
[
-88.134171,
37.583572
],
[
-88.071564,
37.51099
]
]
]]
}
16.3 超越单元测试
要探索程序,还有其他一些方式,下面将介绍两个工具:源代码检查和性能分析。
16.3.1 使用 PyChecker 和 PyLint 检查源代码
长期以来,PyChecker(pychecker.sf.net)都是用于检查Python源代码的唯一工具,能够找出 诸如给函数提供的参数不对等错误。(当然,标准库中还有tabnanny,但没那么强大,只检查缩 进是否正确。)之后出现了PyLint(pylint.org),它支持PyChecker提供的大部分功能,还有很多其 他的功能,如变量名是否符合指定的命名约定、你是否遵守了自己的编码标准等。
安装这些工具后,可以命令行脚本的方式运行它们(PyChecker和PyLint对应的脚本分别为 pychecker和pylint),也可将其作为Python模块(名称与前面相同)。在Windows中,从命令行运行这两个工具时,将分别使用批处理文件pychecker.bat和 pylint.bat。因此,你可能需要将这两个文件加入环境变量PATH中,这样才能从命令行执行 命令pychecker和pylint。
要使用PyChecker来检查文件,可运行这个脚本并将文件名作为参数,如下所示:
pychecker file1.py file2.py ...
使用PyLint检查文件时,需要将模块(或包)名作为参数:
pylint module
PyChecker和PyLint都可作为模块(分别是pychecker.checker和pylint.lint)导入,但它们 并不是为了以编程方式使用而设计的。导入pychecker.checker时,它会检查后续代码(包括导入 的模块),并将警告打印到标准输出。模块pylint.lint包含一个文档中没有介绍的函数Run,这个 函数是供脚本pylint本身使用的。
pychecker目前不支持python3.x。
pylint的使用很简单,pylint目录下(或设置系统路径PATH)执行:
C:\Users\xx\AppData\Roaming\Python\Python37\Scripts>pylint manage.py
************* Module manage
manage.py:1:0: C0111: Missing module docstring (missing-docstring)
-----------------------------------
Your code has been rated at 8.89/10
C:\Users\zhx\AppData\Roaming\Python\Python37\Scripts>
就会看到其执行结果。
注:默认情况下pycharm预装了pylint可以设置在菜单tool下运行。进入PyCharm,从菜单栏,依次进入: File -> Settings -> Tools -> External Tools。“+”,进行添加。需要填写的部分分别是:“Name”,“Tool Settings -> Programs”、“Tool Settings -> Parameters”、“Tool Settings -> Working directory”。“Programs” 为pylint所在的目录;Parameters 为$FilePath$即可在菜单 Tools -> External Tools 中调用。
16.3.2 性能分析
标准库包含一个卓越的性能分析模块profile,还有一个速度更快C语言版本,名为cProfile。 这个性能分析模块使用起来很简单,只需调用其方法run并提供一个字符串参数。
可以在cmd直接运行:
D:\>python -m cProfile test.py
3 function calls in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 test.py:1(<module>)
1 0.000 0.000 0.000 0.000 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Prof
iler' objects}
D:\>
其中:“3 function calls in 0.000 seconds”是说调用了3个函数,用时0.00秒,其下为调用函数运行时间明细列表。
也可以引进入模块,形如:
#ptest.py
def foo():
sum = 0
for i in range(10000):
sum += i
sumA = bar()
sumB = bar()
return sum
def bar():
sum = 0
for i in range(100000):
sum += i
return sum
if __name__ == "__main__":
import cProfile
(待续)