Diango博客--25.使用Coverage統計測試覆蓋率

文章目錄

  • 1. 前言
  • 2. 安裝 Coverage
  • 3. 簡單配置 Coverage
  • 4. 運行 Coverage
  • 5. 完善 Coverage 配置
  • 6. 生成 HTML 報告
  • 7. 完善單元測試

1. 前言

我們完成了對 blog 應用和 comment 應用這兩個核心 app 的測試。現在我們想知道的是究竟測試效果怎么樣呢?測試充分嗎?測試全面嗎?還有沒有沒有測到的地方呢?

單憑肉眼觀察難以回答上面的問題,接下來我們就借助 Coverage.py,從代碼覆蓋率的角度來檢測一下我們的測試效果究竟如何。

Coverage.py (以下簡稱 Coverage)是 Python 測試界最為流行的一個庫之一,用來統計測試覆蓋率。測試覆蓋率可以從一個角度衡量代碼的質量,覆蓋率越高,說明測試越充分,代碼出現 bug 的幾率也就越小。當然需要注意的是,測試覆蓋率僅僅只是衡量代碼質量的一個角度,即使是 100% 的覆蓋率也不能說代碼就是完美的,沒有 bug 的。

2. 安裝 Coverage

要使用 Coverage,首先當然是安裝它:

$ pipenv install coverage --dev

因為只在開發時才用得到,所以使用 Pipenv 安裝時加 --dev 選項將其標記為開發時的依賴庫。

3. 簡單配置 Coverage

Coverage 支持很多配置選項,為了方便,通常將這些配置寫在名為 .coveragerc 的文件中,Coverage 運行時會從項目根目錄讀取這個配置文件。因此先在項目根目錄創建這個文件并寫入最基本的配置:

[run]
branch = True
source = .
[report]
show_missing = True

Coverage 的配置遵循 ini 文件語法。簡單來說就是,[section] 代表一個配置塊,用于組織相關的一組配置。例如這里 [run] 是一個配置塊,[report] 是另一個配置塊,兩個塊下都有相關的一些配置項。配置項的格式為 key = value 。這幾個簡單配置項的含義為:

  1. branch = True 是否統計條件語句的分支覆蓋情況。if 條件語句中的判斷通常有 True 和 False 兩種情況,設置 branch = True 后,Coverage 會測量這兩種情況是否都被測試到。
  2. source =. 指定需統計的源代碼目錄,這里設置為當前目錄(即項目根目錄)。
  3. show_missing = True 在生成的統計報告中顯示未被測試覆蓋到的代碼行號。

4. 運行 Coverage

簡單配置后,我們就可以來運行 Coverage 了。打開命令行,進入項目根目錄,依次運行下面的命令(注意如果沒有激活python shell 虛擬需使用 pipenv run 讓命令在虛擬環境中執行)。

首先運行 erase 命令清除上一次的統計信息

$ pipenv run coverage erase

manage.py test 運行 django 單元測試,這是這一次用 coverage run 來運行

$ pipenv run coverage run manage.py test

生成覆蓋率統計報告

$ pipenv run coverage report

覆蓋率統計報告輸出如下:

Name                                             Stmts   Miss Branch BrPart  Cover   Missing
--------------------------------------------------------------------------------------------
_credentials.py                                      2      2      0      0     0%   1-2
blog\__init__.py                                     0      0      0      0   100%
blog\admin.py                                       11      0      0      0   100%
blog\apps.py                                         4      0      0      0   100%
blog\elasticsearch2_ik_backend.py                    8      0      0      0   100%
blog\feeds.py                                       12      0      0      0   100%
blog\migrations\0001_initial.py                      7      0      0      0   100%
blog\migrations\0002_auto_20190711_1802.py           7      0      0      0   100%
blog\migrations\0003_auto_20191011_2326.py           4      0      0      0   100%
blog\migrations\0004_post_views.py                   4      0      0      0   100%
blog\migrations\__init__.py                          0      0      0      0   100%
blog\models.py                                      62      0      0      0   100%
blog\search_indexes.py                               8      0      0      0   100%
blog\templatetags\__init__.py                        0      0      0      0   100%
blog\templatetags\blog_extras.py                    15      0      0      0   100%
blog\tests\__init__.py                               0      0      0      0   100%
blog\tests\test_models.py                           58      0      2      0   100%
blog\tests\test_smoke.py                             4      0      0      0   100%
blog\tests\test_templatetags.py                    115      0      2      0   100%
blog\tests\test_utils.py                            11      0      0      0   100%
blog\tests\test_views.py                           170      0      8      0   100%
blog\urls.py                                         4      0      0      0   100%
blog\utils.py                                       10      0      2      1    92%   14->16
blog\views.py                                       40      7      2      0    79%   64-72
blogproject\__init__.py                              0      0      0      0   100%
blogproject\settings\__init__.py                     0      0      0      0   100%
blogproject\settings\common.py                      22      0      0      0   100%
blogproject\settings\local.py                        5      0      0      0   100%
blogproject\settings\production.py                   5      5      0      0     0%   1-8
blogproject\urls.py                                  4      0      0      0   100%
blogproject\wsgi.py                                  4      4      0      0     0%   10-16
comments\__init__.py                                 0      0      0      0   100%
comments\admin.py                                    6      0      0      0   100%
comments\apps.py                                     4      0      0      0   100%
comments\forms.py                                    6      0      0      0   100%
comments\migrations\0001_initial.py                  7      0      0      0   100%
comments\migrations\0002_auto_20191011_2326.py       4      0      0      0   100%
comments\migrations\__init__.py                      0      0      0      0   100%
comments\models.py                                  15      0      0      0   100%
comments\templatetags\__init__.py                    0      0      0      0   100%
comments\templatetags\comments_extras.py            12      0      2      0   100%
comments\tests\__init__.py                           0      0      0      0   100%
comments\tests\base.py                              10      0      0      0   100%
comments\tests\test_models.py                        8      0      0      0   100%
comments\tests\test_templatetags.py                 57      0      6      0   100%
comments\tests\test_views.py                        34      0      4      0   100%
comments\urls.py                                     4      0      0      0   100%
comments\views.py                                   17      0      2      0   100%
fabfile.py                                          21     21      0      0     0%   1-43
manage.py                                           12      2      2      1    79%   11-12, 20->exit
scripts\__init__.py                                  0      0      0      0   100%
scripts\fake.py                                     63     63     14      0     0%   1-106
--------------------------------------------------------------------------------------------
TOTAL                                              876    104     46      2    87%

倒數第二列是被統計文件的測試覆蓋率,倒數第一列是未被覆蓋的代碼行號。大部分文件測試覆蓋率為 100%,說明我們的測試還是比較充分的。但從報告結果中我們發現這樣幾個問題:

  1. 有一些文件其實并不需要測試,或者并非項目的核心文件(例如使用fabiric部署項目的腳本 fabfile.py,django 的 migrations 文件等),這些文件應該從統計中排除。
  2. Coverage 默認顯示全部文件的覆蓋率統計結果,如果文件比較多的話就不好查找非 100% 覆蓋率的文件。畢竟我們的目標是提高代碼覆蓋率,因此已達 100% 覆蓋的代碼文件我們不再關心。我們要做的是找到非 100% 覆蓋率的文件,為其添加缺失的測試。

5. 完善 Coverage 配置

可以通過添加 Coverage 配置項輕松解決上面 2 個問題。

在 [run] 配置塊中增加 omit 配置項可以指定排除統計的文件。在 [report] 配置塊中增加 skip_covered 配置項可以指定統計報告中不顯示 100% 覆蓋的文件。

這是 .coveragerc 最終配置結果,注意我們在 omit 配置項中指定忽略了一些非核心的項目文件:

[run]
branch = True
source = .
omit =_credentials.pymanage.pyblogproject/settings/*fabfile.pyscripts/fake.py*/migrations/*blogproject\wsgi.py[report]
show_missing = True
skip_covered = True

再次按照上一節所說的方式運行 Coverage,最終報告結果如下:

Name            Stmts   Miss Branch BrPart  Cover   Missing
-----------------------------------------------------------
blog\utils.py      10      0      2      1    92%   14->16
blog\views.py      40      7      2      0    79%   64-72
-----------------------------------------------------------
TOTAL             709      7     30      1    99%33 files skipped due to complete coverage.

這個報告指出我們仍有 2 個文件沒有達到 100% 的覆蓋率,我們要做的就是為這兩個文件中未測試的代碼增加單元測試,讓其達到 100% 測試覆蓋率。

不過在動手寫測試之前,我們要搞清楚哪些代碼沒被測到。命令行報告的最后一列指出了未被測試代碼的行號,但是這樣看著不是很直觀。一種體驗更好的方式是生成 HTML 報告,這樣我們可以直接在 HTML 報告中查看到未被測試到的具體代碼。

6. 生成 HTML 報告

coverage report 命令在命令行生成統計報告,而 coverage html 則可以生成 HTML 報告。

在上一節的基礎上,運行如下命令:

$ pipenv run coverage html

運行完成后項目根目錄會多出一個 htmlcov 的文件夾,里面就是測試覆蓋率的 HTML 報告文件。用瀏覽器打開里面的 index.html 文件就可以查看報告結果了:
在這里插入圖片描述
主頁和命令行的結果是一樣的,不過我們可以點擊文件名,進入到對這個文件更加具體的統計報告頁面,例如 blog\views.py 結果如下:
在這里插入圖片描述
綠色部分代表已覆蓋的代碼,紅色部分代表未覆蓋的代碼。查看文件我們發現,blog\views.py 中未被覆蓋的代碼原來是 Django 博客實現簡單的全文搜索 中的代碼,現在我們已經將搜索替換為 Django Haystack 全文檢索 了,這段代碼也就不需要了,可以直接刪除。

7. 完善單元測試

blog\utils.py 的報告結果則表明我們在 Django Haystack 全文檢索與關鍵詞高亮 中自定義的搜索關鍵詞高亮器有一個 if 分支條件未被測試到:
在這里插入圖片描述
檢查 blog/tests/test_utils.py 中的測試用例,我們發現只測試了比較短的標題不被截斷,也就是

if len(text_block) < self.max_length:

判斷條件為 True,缺失對判斷條件為 False 的測試。所以我們來構造一個新的測試用例測試標題長度超過 max_length (默認值為 200)的情況時會被截斷:

class HighlighterTestCase(TestCase):def test_highlight(self):# 省略已有代碼 ...highlighter = Highlighter("標題")document = "這是一個長度超過 200 的標題,應該被截斷。" + "HelloDjangoTutorial" * 200self.assertTrue(highlighter.highlight(document).startswith('...<span class="highlighted">標題</span>,應該被截斷。'))

再次運行 Coverage 生成報告,測試覆蓋率全都 100% 了!

$ pipenv run coverage erase
$ pipenv run coverage run manage.py test
$ pipenv run coverage report
# 輸出
Name    Stmts   Miss Branch BrPart  Cover   Missing
---------------------------------------------------
---------------------------------------------------
TOTAL     704      0     28      0   100%

最后提醒一點,Coverage 運行后可能會在項目目錄下生成一些文件,這些文件并不需要納入版本管理,所以將其加入 .gitignore 文件中,防止被提交到代碼庫:

htmlcov/
.coverage
.coverage.*
coverage.xml
*.cover

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/452105.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/452105.shtml
英文地址,請注明出處:http://en.pswp.cn/news/452105.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

面向對象語言的優點

1.一致的表達方法 從前面章節的講述中可以知道&#xff0c;面向對象開發基于不隨時間變化的、一致的表示方法。這種表示方法應該從問題域到OOA&#xff0c;從OOA到OOD&#xff0c;最后從OOD到面向對象編程(OOP)&#xff0c;始終穩定不變。 一致的表示方法&#xff1a; 既有利…

最好的英文詞典

辭典對于學外語的作用&#xff0c;怎么強調也不過分。經常接觸英語的人都知道&#xff0c;遇到生詞不可怕&#xff0c;可怕的是遇到認識的單詞&#xff0c;又不明白這句話什么意思。這個時候&#xff0c;辭典的作用就發揮出來了。 今天一位朋友問我一句英文的意思&#xff0c;…

oracle用戶創建及權限設置

權限&#xff1a; create session create table unlimited tablespace connect resource dba 例&#xff1a; #sqlplus /nolog SQL> conn / as sysdba; SQL>create user username identified by password SQL> grant dba to username; SQL> conn username/password…

Android動畫之逐幀動畫(FrameAnimation)詳解

今天我們就來學習逐幀動畫,廢話少說直接上效果圖如下: 幀動畫的實現方式有兩種&#xff1a; 一、在res/drawable文件夾下新建animation-list的XML實現幀動畫 1、首先在res/drawable文件夾下添加img00-img24共25張圖片 2、新建frame_anim.xml [html] view plaincopy <?xml v…

ajax-簡單參數方法實現陰影效果

注&#xff1a; 簡單參數 &#xff08;按照參數的數量和位置傳遞參數&#xff09; 使用時按照位置、數量傳遞 shadow.js函數//簡單參數實現方式/** slices:陰影* opacity:透明度* zIndex:層級* */jQuery.fn.shadow_simple function (slices,opacity,zIndex) { //獲取到每個…

第一二三范式的簡單理解

第一范式&#xff08;無重復的列&#xff09; 定義&#xff1a;數據庫表的每一列都是不可分割的原子數據項&#xff0c;而不能是集合&#xff0c;數組&#xff0c;記錄等非原子數據項。如果實體中的某個屬性有多個值時&#xff0c;必須拆分為不同的屬性 通俗解釋&#xff1a;一…

網絡爬蟲--1.通用爬蟲和聚焦爬蟲

文章目錄一.前言二.通用爬蟲1.工作原理2.通用爬蟲的局限性三.聚焦爬蟲一.前言 根據使用場景&#xff0c;網絡爬蟲可分為 通用爬蟲 和 聚焦爬蟲 兩種。 其中通用網絡爬蟲是捜索引擎抓取系統&#xff08;Baidu、Google、Yahoo等&#xff09;的重要組成部分。主要目的是將互聯網…

敏捷教練的工具箱

學習并不是簡簡單單的閱讀和瀏覽&#xff0c;而是一個積累的過程&#xff0c;一個通過持續的學習&#xff0c;對自己的知識體系不斷豐富、索引的過程。接下來我會從四個方面入手分享我的經驗。 高質量的信息源和高效的學習 Google是一個很好的工具&#xff0c;通過它&#x…

log4j教程

詳細的Log4j使用教程 轉載 2016年08月19日 14:44:49 5072 日志是應用軟件中不可缺少的部分&#xff0c;Apache的開源項目log4j是一個功能強大的日志組件,提供方便的日志記錄。在apache網站&#xff1a;jakarta.apache.org/log4j 可以免費下載到Log4j最新版本的軟件包。…

BC范式介紹

設關系模式R<U&#xff0c;F>∈1NF&#xff0c;如果對于R的每個函數依賴X→Y&#xff0c;若Y不屬于X&#xff0c;則X必含有候選碼&#xff0c;那么R∈BCNF。 即為&#xff1a;對于關系模式R&#xff0c;若 R為第一范式&#xff0c;且每個屬性都不部分依賴于候選鍵也不傳遞…

com.jhlabs:imaging:jar:01012005 所在倉庫+captcha驗證碼maven依賴

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 <repositories> <repository> <id>atlassian</id> <name>atlassian</name&g…

python 發送郵件的兩種方式【終極篇】

python 發送郵件的兩種方式【終極篇】 一&#xff0c;利用python自帶的庫 smtplib簡單高效 from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.header import Header import smtplib from django.conf import settingsmail_hos…

網絡爬蟲--2.HTTP和HTTPS

文章目錄一.簡介二.HTTP的請求與響應三.客戶端HTTP請求1.格式2.請求方法四.常用的請求報頭1.Host (主機和端口號)2.Connection (鏈接類型)3.Upgrade-Insecure-Requests (升級為HTTPS請求)4. User-Agent (瀏覽器名稱)5. Accept (傳輸文件類型)6.Referer (頁面跳轉處)7.Accept-En…

解決win7的outlook打不開的問題

outlook打不開&#xff0c;一直顯示正在處理 解決方法&#xff1a; 1. 按住Ctrl,雙擊打開組件&#xff0c;會提示是否進入安全模式&#xff0c; 進入安全模式 2. 單擊Outlook中的文件-選項-加載項- 左下角的“COM加載項“ 旁邊的“轉到”&#xff0c;將所有加載項前面的勾都去掉…

IBM王陽:軟件是凝聚創新力的最佳平臺

導讀&#xff1a;在IBM全球副總裁兼IBM中國開發中心總經理王陽博士看來&#xff0c;IBM百年不衰的根本原因在于將創新力凝結成軟件然后進行合適的傳播&#xff0c;其間最重要的是成功打造出了一個吸引人才、培養研發人才并激發出人才創新力的環境和氛圍。而保持創新領導力的關鍵…

數據庫的規范化

在關系數據庫中&#xff0c;對關系模式的基本要求是滿足第一范式。 規范化程度過低的關系不一定能夠很好地描述現實世界 可能存在插入異常、刪除異常、修改復雜、數據冗余等問題 解決方法就是對其進行規范化&#xff0c;轉換成高級范式 一個低一級范式的關系模式&#xff0c;通…

Jquery 多行拖拽圖片排序 jq優化

<!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <title>jQuery圖片拖動排序代碼</title><style type"text/css">.item_container{position:relative;height:auto;overflow:hidden;} .item_content ul{li…

應該把script標簽放在哪里

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 概述&#xff1a; 如果在頁面中寫JS的話&#xff0c;那必然會用到script標簽&#xff0c;理論上script標簽放在哪里都是可以的&#xff…

網絡爬蟲--3.str和bytes的區別

文章目錄一.bytes二.str和bytes相互轉換三.bytearray一.bytes bytes對象只負責以二進制字節序列的形式記錄所需記錄的對象&#xff0c;至于該對象到底表示什么&#xff08;比如到底是什么字符&#xff09;則由相應的編碼格式解碼所決定。 bytes是Python 3中特有的&#xff0c…

git使用問題

1、錯誤&#xff1a;The following untracked working tree files would be overwritten by checkout 。后面跟了幾個文件 場景&#xff1a;需要從一個分支切換到另一個分支時報錯 方法&#xff1a;git clean -d -fx "" 原因&#xff1a;之前修改了.gitignore文件&am…