第十六章 測試基礎

第十六章 測試基礎

在編譯型語言中,需要不斷重復編輯、編譯、運行的循環。
在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.pswp.cn/news/378263.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/378263.shtml
英文地址,請注明出處:http://en.pswp.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 轉換成…