第十六章 测试基础

第十六章 测试基础

在编译型语言中,需要不断重复编辑、编译、运行的循环。
在Python中,不存在编译阶段只有编辑和运行阶段。测试就是运行程序

先测试再编码

极限编程先锋引入了“测试一点点,再编写一点点代码”的理念。
换而言之,测试在先,编码在后。这也称为测试驱动的编程。

准确的需求说明

要阐明程序的目标,可编写需求说明,也就是描述程序必须满足何种需求的文档(或便条)。
测试程序就是需求说明,可帮助确保程序开发过程紧扣这些需求。

假设你要编写一个模块,其中只包含一个根据矩形的宽度和高度计算面积的函数。动手编写代码前,编写一个单元测试,其中包含一些你知道答案的例子。

文件area.py内容如下:

def rect_area(height,width):return height*height #很显然不对

同目录下的test.py内容如下:

from area import rect_area
height = 3
width = 4
correct_answer = 12
answer = rect_area(height,width)
if answer == correct_answer:print('Test passed')
else:print('Test failed')'''
很显然,输出结果为:Test failed
接下来,你可能检查代码,看看问题出在什么地方,并将返回的表达式替换为height * width。
'''

做好应对变化的准备

自动化测试不仅可在你编写程序时提供极大的帮助,还有助于在你修改代码时避免累积错误,这在程序规模很大时尤其重要。

代码覆盖率
覆盖率(coverage)是一个重要的测试概念。运行测试时,很可能达不到运行所有代码的理想状态。(实际上,最理想的情况是,使用各种可能的输入检查每种可能的程序状态,但这根本不可能做到。)优秀测试套件的目标之一是确保较高的覆盖率,为此可使用覆盖率工具,它们测量测试期间实际运行的代码所占的比例。
Python自带的程序trace.py。
要确保较高的测试覆盖率,方法之一是秉承测试驱动开发的理念。只要能确保先编写测试再编写函数,就能肯定每个函数都是经过测试的。

测试四部曲

1,确定需要实现的新功能。可将其记录下来,再为之编写一个测试。
2,编写实现功能的框架代码,让程序能够运行(不存在语法错误之类的问题),但测试依然无法通过。
3, 编写让测试刚好能够通过的代码。
4,改进(重构)代码以全面而准确地实现所需的功能,同时确保测试依然能够成功。

测试工具

doctest

文件my_math.py

def square(x):return x * xif name == '__main__':import doctest, my_mathdoctest.testmod(my_math)

对模块doctest中的函数testmod进行测试
python my_math.py
先显然,并没有什么显示输出
函数doctest.testmod读取模块中的所有文档字符串,查找看起来像是从交互式解释器中摘取的示例,再检查这些示例是否反映了实际情况。

为获得更多的输出,可在运行脚本时指定开关-v(verbose,意为详尽)。
python my_math.py -v
输入如下:

Running my_math.__doc__ 
0 of 0 examples failed in my_math.__doc__ 
Running my_math.square.__doc__ 
Trying: square(2) 
Expecting: 4 
Ok 
Trying: square(3) 
Expecting: 9 
ok 
0 of 2 examples failed in my_math.square.__doc__ 
1 items had no tests: test 
1 items passed all tests: 
2 tests in my_math.square 
2 tests in 2 items. 
2 passed and 0 failed. 
Test passed.

假设要使用Python幂运算符而不是乘法运算符,将x * x替换为x ** 2,再运行脚本对代码进行测试。
输出如下:

***************************************************************** 
Failure in example: square(3) 
from line #5 of my_math.square 
Expected: 9 
Got: 27 
*****************************************************************
1 items had failures: 1 of 2 in my_math.square 
***Test Failed*** 
1 failures.

unittest

doctest使用起来很容易,但unittest(基于流行的Java测试框架JUnit)更灵活、更强大。

一个使用框架unittest的简单测试

import unittest, my_math
class ProductTestCase(unittest.TestCase):def test_integers(self):for x in range(-10, 10):for y in range(-10, 10):p = my_math.product(x, y)self.assertEqual(p, x * y, 'Integer multiplication failed')def test_floats(self):for x in range(-10, 10):for y in range(-10, 10):x = x / 10y = y / 10p = my_math.product(x, y)self.assertEqual(p, x * y, 'Float multiplication failed')if __name__ == '__main__': unittest.main()

运行这个测试脚本将引发异常,指出模块my_math不存在。

模块unittest区分错误和失败。错误指的是引发了异常,而失败是调用failUnless等方法的结果。

文件my_math.py

def product(x,y):pass#框架代码,没什么意思。

运行前面的测试,将出现两条FAIL消息,输出如下:

FF 
====================================================================== 
FAIL: test_floats (__main__.ProductTestCase) 
---------------------------------------------------------------------- 
Traceback (most recent call last): File "test_my_math.py", line 17, in testFloats self.assertEqual(p, x * y, 'Float multiplication failed') 
AssertionError: Float multiplication failed 
====================================================================== 
FAIL: test_integers (__main__.ProductTestCase) 
---------------------------------------------------------------------- 
Traceback (most recent call last): File "test_my_math.py", line 9, in testIntegers self.assertEqual(p, x * y, 'Integer multiplication failed') 
AssertionError: Integer multiplication failed 
---------------------------------------------------------------------- 
Ran 2 tests in 0.001s 
FAILED (failures=2)

开头两个字符,两个F,表示两次失败。

接下来需要让代码管用。修改文件my_math.py

def product(x,y):return x * y

再次运行前面的测试,输出如下:

.. 
---------------------------------------------------------------------- 
Ran 2 tests in 0.015s 
OK

开头的两个句点表示测试。

再次修改函数product,即修改文件my_math.py

def product(x, y):if x == 7 and y == 9:return 'An insidious bug has surfaced!'else:return x * y

再次运行前面的测试脚本,将有一个测试失败。输出如下:

.F 
====================================================================== 
FAIL: test_integers (__main__.ProductTestCase) 
---------------------------------------------------------------------- 
Traceback (most recent call last): File "test_my_math.py", line 9, in testIntegers self.assertEqual(p, x * y, 'Integer multiplication failed') 
AssertionError: Integer multiplication failed 
---------------------------------------------------------------------- 
Ran 2 tests in 0.005s 
FAILED (failures=1)

超越单元测试

两个工具:源代码检查和性能分析。
源代码检查是一种发现代码中常见错误或问题的方式(有点像静态类型语言中编译器的作用,但做的事情要多得多)。
性能分析指的是搞清楚程序的运行速度到底有多快。

使用PyChecker 和 PyLint 检查源代码

PyChecker(pychecker.sf.net)用于检查Python源代码的唯一工具,能够找出诸如给函数提供的参数不对等错误。
标准库中还有tabnanny,但没那么强大,只检查缩进是否正确。

之后出现了PyLint(pylint.org),它支持PyChecker提供的大部分功能,还有很多其他的功能,如变量名是否符合指定的命名约定、是否遵守了自己的编码标准等。

使用Distutils来安装,可使用如下标准命令。
python setup.py install
PyLint,也可使用pip来安装。

使用PyChecker来检查文件,可运行这个脚本并将文件名作为参数
pychecker file1.py file2.py ...

使用PyLint检查文件时,需要将模块(或包)名作为参数:pylint module

PyChecker和PyLint都可作为模块(分别是pychecker.checker和pylint.lint)导入

导入pychecker.checker时,它会检查后续代码(包括导入的模块),并将警告打印到标准输出。

模块pylint.lint包含一个文档中没有介绍的函数Run,这个函数是供脚本pylint本身使用的。它也将警告打印出来,而不是以某种方式将其返回。

使用模块subprocess调用外部检查器

import unittest, my_math 
from subprocess import Popen, PIPEclass ProductTestCase(unittest.TestCase):def test_with_PyChecker(self):cmd = 'pychecker', '-Q', my_math.__file__.rstrip('c')pychecker = Popen(cmd, stdout=PIPE, stderr=PIPE)self.assertEqual(pychecker.stdout.read(), '')def test_with_PyLint(self):cmd = 'pylint', '-rn', 'my_math'pylint = Popen(cmd, stdout=PIPE, stderr=PIPE)self.assertEqual(pylint.stdout.read(), '')if __name__ == '__main__': unittest.main()

对于pychecker,开关-Q(quiet,意为静默);
对于pylint,开关-rn(其中n表示no)以关闭报告,这意味着将只显示警告和错误。

命令pylint直接将模块名作为参数
让pychecker正确地运行,需要获取文件名。使用了模块my_math的属性__file__,并使用rstrip将文件名末尾可能包含的c删掉(因为模块可能存储在.pyc文件中)

模块my_math,文件my_math.py

__revision__ = '0.1'
def product(factor1, factor2):'The product of two numbers'return factor1 * factor2

性能分析

在编程中,不成熟的优化是万恶之源。
如果程序的速度达不到你的要求,必须优化,就必须首先对其进行性能分析。

标准库包含一个卓越的性能分析模块profile,还有一个速度更快C语言版本,名为cProfile。
这个性能分析模块使用起来很简单,只需调用其方法run并提供一个字符串参数。
这里照样使用了以前的文件my_math.py

import cProfile
from my_math import product
cProfile.run('product(1, 2)')

这将输出如下信息:各个函数和方法被调用多少次以及执行它们花费了多长时间。如果通过第二个参数向run提供一个文件名(如’my_math.profile’),分析结果将保存到这个文件中。然后,就可使用模块pstats来研究分析结果了。

import pstats
p = pstats.Stats('my_math.profile')

小结

概念描述
测试驱动编程大致而言,测试驱动编程意味着先测试再编码。有了测试,就能信心满满地修改代码,这让开发和维护工作更加灵活。
模块doctest和unittest需要在Python中进行单元测试时,这些工具必不可少。模块doctest设计用于检查文档字符串中的示例,但也可轻松地使用它来设计测试套件。为让测试套件更灵活、结构化程度更高,框架unittest很有帮助。
PyChecker和PyLint这两个工具查看源代码并指出潜在(和实际)的问题。它们检查代码的方方面面——从变量名太短到永远不会执行的代码段。只需编写少量的代码,就可将它们加入测试套件,从而确保所有修改和重构都遵循了你采用的编码标准。
性能分析如果很在乎速度,并想对程序进行优化(仅当绝对必要时才这样做),应首先进行性能分析:使用模块profile或cProfile来找出代码中的瓶颈。

本章介绍的新函数

函数描述
doctest.testmod(module)检查文档字符串中的示例(还接受很多其他的参数)
unittest.main()运行当前模块中的单元测试
profile.run(stmt[,filename])执行语句并对其进行性能分析;可将分析结果保存到参数filename指定的文件中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/378263.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何蹭网

引言蹭网,在普通人的眼里,是一种很高深的技术活,总觉得肯定很难,肯定很难搞。还没开始学,就已经败给了自己的心里,其实,蹭网太过于简单。我可以毫不夸张的说,只要你会windows的基本操…

android对象缓存,Android简单实现 缓存数据

前言1、每一种要缓存的数据都是有对应的versionCode,通过versionCode请求网络获取是否需要更新2、提前将要缓存的数据放入assets文件夹中,打包上线。缓存设计代码实现/*** Created by huangbo on 2017/6/19.** 主要是缓存的工具类** 缓存设计&#xff1a…

通信原理.绪论

今天刚上通信原理的第一节课,没有涉及过多的讲解,只是讲了下大概的知识框架。现记录如下: 目录1、基本概念消息、信息与信号2、通信系统模型1、信息源2、发送设备3、信道4、接收设备5、信宿6、模拟通信系统模型7、数字通信系统模型8、信源编…

Android版本演进中的兼容性问题

原文:http://android.eoe.cn/topic/summary Android 3.0 的主要变化包括: 不再使用硬件按键进行导航 (返回、菜单、搜索和主屏幕),而是采用虚拟按键 (返回,主屏幕和最近的应用)。在操作栏固定菜单。 Android 4.0 则把这些变化带到了手机平台。…

css rgba透明_rgba()函数以及CSS中的示例

css rgba透明Introduction: 介绍: Functions are used regularly while we are developing a web page or website. Therefore, to be a good developer you need to master as many functions as you can. This way your coding knowledge will increase as well …

第十七章 扩展Python

第十七章 Python什么都能做,真的是这样。这门语言功能强大,但有时候速度有点慢。 鱼和熊掌兼得 本章讨论确实需要进一步提升速度的情形。在这种情况下,最佳的解决方案可能不是完全转向C语言(或其他中低级语言)&…

android studio资源二进制,无法自动检测ADB二进制文件 – Android Studio

我尝试在Android Studio上测试我的应用程序,但我遇到了困难"waiting for AVD to come online..."我读过Android设备监视器重置adb会做到这一点,它确实……对于1次测试,当我第二天重新启动电脑时,我不仅仅是:"waiting for AVD to come online..."…

犀牛脚本:仿迅雷的增强批量下载

迅雷的批量下载满好用。但是有两点我不太中意。在这个脚本里会有所增强 1、不能设置保存的文件名。2、不能单独设置这批下载的线程限制。 使用方法 // 下载从编号001到编号020的图片,保存名为猫咪写真*.jpg 使用6个线程 jdlp http://bizhi.zhuoku.com/bizhi/200804/…

为什么使用1 * 1 的卷积核

为什么使用 1* 1卷积核? 作用: 1、实现跨通道的交互和信息整合 2、 进行卷积核通道数的降维和升维 3、 在保持feature map尺度不变的(即不损失分辨率)的前提下大幅增加非线性特性 跨通道的交互和信息整合 使用1 * 1卷积核&a…

KingPaper初探ThinkPHP3.1.2之扩展函数库和类库的使用(四)

在我们做项目的时候TP的系统函数或系统类库满足不了我们的需要 所以有些东西需要我们自己去定义,在TP中我们怎么使用自己的函数库和类库呢?在TP系统中提供了三个系统函数库 common.php是全局必须加载的基础函数库,在任何时候都可以直接调用&a…

isfinite函数_isfinite()函数以及C ++中的示例

isfinite函数C isfinite()函数 (C isfinite() function) isfinite() function is a library function of cmath header, it is used to check whether the given value is a finite value or not? It accepts a value (float, double or long double) and returns 1 if the v…

android 服务端 漏洞,安卓漏洞 CVE 2017-13287 复现详解-

2018年4月,Android安全公告公布了CVE-2017-13287漏洞。与同期披露的其他漏洞一起,同属于框架中Parcelable对象的写入(序列化)与读出(反序列化)的不一致所造成的漏洞。在刚看到谷歌对于漏洞给出的补丁时一头雾水,在这里要感谢heeeeenMS509Team…

某公司面试题

一、基础题 1,冯诺依曼结构的计算机硬件逻辑组成中,不包含以下哪个模块? A,编译器 B,控制器 C,输入设备 D,输出设备 解释:冯诺依曼由五个模块组成:输入设备 输出设备 存…

GAP(全局平均池化层)操作

转载的文章链接: 为什么使用全局平均池化层? 关于 global average pooling https://blog.csdn.net/qq_23304241/article/details/80292859 在卷积神经网络的初期,卷积层通过池化层(一般是 最大池化)后总是要一个或n个全…

zoj1245 Triangles(DP)

/* 动态三角形&#xff1a;每次DP时考虑的是两个子三角形的高度即可 注意&#xff1a; 三角形可以是倒置的。 */ View Code 1 #include <iostream> 2 #include <cstdlib> 3 #include <cstring> 4 #include <stdio.h> 5 6 using namespace std; 7 8…

第十八章 程序打包

第十八章 程序打包 Setuptools和较旧的Distutils都是用于发布Python包的工具包&#xff0c;能够使用Python轻松地编写安装脚本。这些脚本可用于生成可发布的归档文档&#xff0c;供用户用来编译和安装编写库。 Setuptools并非只能用于创建基于脚本的Python安装程序&#xff0…

如何在Java中检查对象是否为空?

With the help of "" operator is useful for reference comparison and it compares two objects. 借助“ ”运算符&#xff0c;对于参考比较非常有用&#xff0c;它可以比较两个对象。 "" operator returns true if both references (objects) points to…

android编程从零开始,从零开始学习android开发

博主最近开通了Android栏目&#xff0c;现在正在从零开始学习android&#xff0c;遇到的所有值得分享的知识点以及遇到的问题将发布在这个博客的android栏目下。因为我有着深厚的java底子&#xff0c;所以学习起来得心应手&#xff0c;十分的简单&#xff0c;当然也只能算是入门…

CNN基本步骤以及经典卷积(LeNet、AlexNet、VGGNet、InceptionNet 和 ResNet)网络讲解以及tensorflow代码实现

课程来源&#xff1a;人工智能实践:Tensorflow笔记2 文章目录前言1、卷积神经网络的基本步骤1、卷积神经网络计算convolution2、感受野以及卷积核的选取3、全零填充Padding4、tf描述卷积层5、批标准化(BN操作)6、池化Pooling7、舍弃Dropout8、卷积神经网络搭建以及参数分析2、经…

String.valueOf()

1. 由 基本数据型态转换成 String String 类别中已经提供了将基本数据型态转换成 String 的 static 方法 也就是 String.valueOf() 这个参数多载的方法 有下列几种 String.valueOf(boolean b) : 将 boolean 变量 b 转换成字符串 String.valueOf(char c) : 将 char 变量 c 转换成…