如何測試代碼覆蓋率:coverage.py 簡介
本文作者為 Mike Driscoll,譯者為 linkcheng,校對 EarlGrey,是 Python 翻譯組完成的第二篇譯文。本文為編程派微信公眾號首發。
Coverage.py是一個用來測試代碼覆蓋率的 Python 第三方庫。它起初是由 Ned Batchelder 創建。在編程界,術語“覆蓋”通常是用來描述測試的有效性,以及測試的實際覆蓋率。coverage.py 庫支持 Python 2.6 或者更高的版本,還兼容 Python 3 的最新版以及 PyPy 。
pip install coverage
執行以上指令來安裝 coverage.py ,不過我們需要寫一些代碼才能使用它。創建一個名為 mymath.py 的模塊,代碼如下:
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiply(a, b):
return a * b
def divide(numerator, denominator):
return float(numerator) / denominator
現在需要一個測試文件。接下來,我們創建一個測試 add 函數的測試文件,并將其命令為 test_mymath.py 。然后把它保存在與 mymath.py 的相同目錄下。接著在測試文件中寫入以下代碼:
# test_mymath.py
import mymath
import unittest
class TestAdd(unittest.TestCase):
"""
Test the add function from the mymath library
"""
def test_add_integers(self):
"""
Test that the addition of two integers returns the correct total
"""
result = mymath.add(1, 2)
self.assertEqual(result, 3)
def test_add_floats(self):
"""
Test that the addition of two floats returns the correct result
"""
result = mymath.add(10.5, 2)
self.assertEqual(result, 12.5)
def test_add_strings(self):
"""
Test the addition of two strings returns the two string as one
concatenated string
"""
result = mymath.add('abc', 'def')
self.assertEqual(result, 'abcdef')
if __name__ == '__main__':
unittest.main()
一切準備就緒,讓我們使用測試文件來運行 coverage.py。打開終端并且進入我們剛才寫的那兩個文件所在的目錄。然后通過以下方式執行 coverage.py:
coverage run test_mymath.py
注意,我們需要調用 run 才能讓 coverage.py 運行指定的模塊。如果模塊接收參數,可以像正常運行這個的模塊一樣帶上參數。當執行以上指令后,你會看到測試模塊的輸出,就像正常運行該模塊一樣。在當前目錄下,你還會發現一個名字為 .coverage 的文件(注意開頭的點號)。要想獲得文件中的信息,需要執行以下指令:
coverage report -m
執行這條指令將會在終端打印以下信息:
Name Stmts Miss Cover Missing
----------------------------------------------
mymath.py 9 3 67% 9, 13, 17
test_mymath.py 14 0 100%
----------------------------------------------
TOTAL
-m選項告訴 coverage.py 你想在輸出信息中顯示 Missing 列。如果省略 -m 選項,就只能看到前四列信息。上面的輸出表明,coverage 在執行完測試代碼之后,判斷我寫的單體測試程序對 mymath 模塊的覆蓋率只有 67% 。 “Missing” 列表明哪些行代碼沒有被覆蓋。如果你看過 coverage.py 指出的那些行代碼,很快就會發現測試程序沒有運行測試 subtract , multiply 和 divide 函數。
在嘗試添加更多的覆蓋率測試代碼之前,先來學習一下怎么通過 coverage.py 來生成 HTML報告。只需要執行以下命令即可:
coverage html
以上指令將會生成一個叫 htmlcov 的目錄,其中包括各種各樣的文件。進入這個目錄,并通過瀏覽器打開 index.html 文件。在我的電腦上,瀏覽器加載了這樣的頁面:
實際上,你可以通過點擊 Module 列中列出的文件名來打開一個新的頁面,頁面中將會明顯標識出代碼中沒有被單體覆蓋的部分。顯然 mymath.py 的覆蓋率不夠高,所以點擊 mymath.py ,頁面最終顯示如下:
以上截圖清晰地展示了沒有被單體測試所覆蓋的部分。現在我們清楚地知道測試覆蓋有哪些缺失了,接下來就給 subtract 函數添加單體測試,并且看一下覆蓋率的改變。
打開 test_mymath.py 并且添加下邊的類:
class TestSubtract(unittest.TestCase):
"""
Test the subtract function from the mymath library
"""
def test_subtract_integers(self):
"""
Test that subtracting integers returns the correct result
"""
result = mymath.subtract(10, 8)
self.assertEqual(result, 2)
現在我們需要重新對更新后的測試文件運行 coverage。你只需要再次運行該命令即可: coverage run test_mymath.py 。命令輸出將指出成功通過了四個測試。接著,重新運行 coverage html ,再打開 index.html 文件。你應該會看到我們達到了 78% 的覆蓋率:
這次修改讓覆蓋率提高了 11% !接下來,讓我們給 multiply 和 divide 函數添加簡單的測試,看覆蓋率能否達到 100% !
class TestMultiply(unittest.TestCase):
"""
Test the multiply function from the mymath library
"""
def test_subtract_integers(self):
"""
Test that multiplying integers returns the correct result
"""
result = mymath.multiply(5, 50)
self.assertEqual(result, 250)
class TestDivide(unittest.TestCase):
"""
Test the divide function from the mymath library
"""
def test_divide_by_zero(self):
"""
Test that multiplying integers returns the correct result
"""
with self.assertRaises(ZeroDivisionError):
result = mymath.divide(8, 0)
再次運行之前運行過的命令,然后再重新打開 “index.html”。然后就會看到如下截圖:
正如你看到的那樣,這次我們的覆蓋率達到了 100%!顯然,覆蓋率 100% 意味著我們測試程序測試了每一個需要被測試的函數。當然這也有些不盡人意的地方,比如:add 函數的單體測試數量是其他幾個函數的三倍,然而 coverage.py 并沒有給出關于這些的詳細信息。盡管 coverage.py 不能詳盡說明我們是否測試了所有可能的參數組合情況,但卻可以明確反映關于覆蓋率的一些基本信息。
額外信息
順便再簡單提及一些 coverage.py 的其他特性。首先,coverage.py 支持配置文件。配置文件格式是傳統的“.ini”文件,使用中括號作為節與節的分界(例如:[my_section])。還可以使用 # 或者 ; (分號)來添加注釋。
Coverage.py 也允許在上述提到的配置文件中指定你需要解析的源文件。一旦在配置文件中設置了需要解析的文件,就可以通過運行 coverage.py 來看運行結果。它還支持“-source”命令行選項。最后,還可以使用“-include”和“-omit”選項來包含或者移除一個文件名模式的列表。這些選項也可以通過在配置文件中添加對應的配置項進行設置。
關于 coverage.py 想最后再說明一點,就是它支持插件。你可以自己寫插件,也可以從網上下載并安裝別人的插件來增強 coverage.py 的功能。
總結
現在你已經了解 coverage.py 的基本情況以及它的一些用途。Coverage.py 可以檢測單體測試代碼并且發現單體測試覆蓋中的漏洞。如果不確定你的單體測試程序是否達標,那么使用這個庫包將會幫助你找到那些存在的漏洞。即便如此,你仍然需要認真負責地編寫高質量的測試程序。如果沒有寫出有效的測試,而且測試還通過了,那么 coverage.py 也無法幫到你。
點此查看原文鏈接 。
Python 翻譯組 是EarlGrey@編程派發起成立的一個專注于 Python 技術內容翻譯的小組,目前已有近 30 名 Python 技術愛好者加入。
來自:http://www.codingpy.com/article/an-intro-to-coveragepy/