Diango博客--25.使用Coverage统计测试覆盖率
文章目錄
- 1. 前言
- 2. 安裝 Coverage
- 3. 簡(jiǎn)單配置 Coverage
- 4. 運(yùn)行 Coverage
- 5. 完善 Coverage 配置
- 6. 生成 HTML 報(bào)告
- 7. 完善單元測(cè)試
1. 前言
我們完成了對(duì) blog 應(yīng)用和 comment 應(yīng)用這兩個(gè)核心 app 的測(cè)試。現(xiàn)在我們想知道的是究竟測(cè)試效果怎么樣呢?測(cè)試充分嗎?測(cè)試全面嗎?還有沒(méi)有沒(méi)有測(cè)到的地方呢?
單憑肉眼觀察難以回答上面的問(wèn)題,接下來(lái)我們就借助 Coverage.py,從代碼覆蓋率的角度來(lái)檢測(cè)一下我們的測(cè)試效果究竟如何。
Coverage.py (以下簡(jiǎn)稱 Coverage)是 Python 測(cè)試界最為流行的一個(gè)庫(kù)之一,用來(lái)統(tǒng)計(jì)測(cè)試覆蓋率。測(cè)試覆蓋率可以從一個(gè)角度衡量代碼的質(zhì)量,覆蓋率越高,說(shuō)明測(cè)試越充分,代碼出現(xiàn) bug 的幾率也就越小。當(dāng)然需要注意的是,測(cè)試覆蓋率僅僅只是衡量代碼質(zhì)量的一個(gè)角度,即使是 100% 的覆蓋率也不能說(shuō)代碼就是完美的,沒(méi)有 bug 的。
2. 安裝 Coverage
要使用 Coverage,首先當(dāng)然是安裝它:
$ pipenv install coverage --dev因?yàn)橹辉陂_發(fā)時(shí)才用得到,所以使用 Pipenv 安裝時(shí)加 --dev 選項(xiàng)將其標(biāo)記為開發(fā)時(shí)的依賴庫(kù)。
3. 簡(jiǎn)單配置 Coverage
Coverage 支持很多配置選項(xiàng),為了方便,通常將這些配置寫在名為 .coveragerc 的文件中,Coverage 運(yùn)行時(shí)會(huì)從項(xiàng)目根目錄讀取這個(gè)配置文件。因此先在項(xiàng)目根目錄創(chuàng)建這個(gè)文件并寫入最基本的配置:
[run] branch = True source = . [report] show_missing = TrueCoverage 的配置遵循 ini 文件語(yǔ)法。簡(jiǎn)單來(lái)說(shuō)就是,[section] 代表一個(gè)配置塊,用于組織相關(guān)的一組配置。例如這里 [run] 是一個(gè)配置塊,[report] 是另一個(gè)配置塊,兩個(gè)塊下都有相關(guān)的一些配置項(xiàng)。配置項(xiàng)的格式為 key = value 。這幾個(gè)簡(jiǎn)單配置項(xiàng)的含義為:
4. 運(yùn)行 Coverage
簡(jiǎn)單配置后,我們就可以來(lái)運(yùn)行 Coverage 了。打開命令行,進(jìn)入項(xiàng)目根目錄,依次運(yùn)行下面的命令(注意如果沒(méi)有激活python shell 虛擬需使用 pipenv run 讓命令在虛擬環(huán)境中執(zhí)行)。
首先運(yùn)行 erase 命令清除上一次的統(tǒng)計(jì)信息
$ pipenv run coverage erasemanage.py test 運(yùn)行 django 單元測(cè)試,這是這一次用 coverage run 來(lái)運(yùn)行
$ pipenv run coverage run manage.py test生成覆蓋率統(tǒng)計(jì)報(bào)告
$ pipenv run coverage report覆蓋率統(tǒng)計(jì)報(bào)告輸出如下:
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%倒數(shù)第二列是被統(tǒng)計(jì)文件的測(cè)試覆蓋率,倒數(shù)第一列是未被覆蓋的代碼行號(hào)。大部分文件測(cè)試覆蓋率為 100%,說(shuō)明我們的測(cè)試還是比較充分的。但從報(bào)告結(jié)果中我們發(fā)現(xiàn)這樣幾個(gè)問(wèn)題:
5. 完善 Coverage 配置
可以通過(guò)添加 Coverage 配置項(xiàng)輕松解決上面 2 個(gè)問(wèn)題。
在 [run] 配置塊中增加 omit 配置項(xiàng)可以指定排除統(tǒng)計(jì)的文件。在 [report] 配置塊中增加 skip_covered 配置項(xiàng)可以指定統(tǒng)計(jì)報(bào)告中不顯示 100% 覆蓋的文件。
這是 .coveragerc 最終配置結(jié)果,注意我們?cè)?omit 配置項(xiàng)中指定忽略了一些非核心的項(xiàng)目文件:
[run] branch = True source = . omit =_credentials.pymanage.pyblogproject/settings/*fabfile.pyscripts/fake.py*/migrations/*blogproject\wsgi.py[report] show_missing = True skip_covered = True再次按照上一節(jié)所說(shuō)的方式運(yùn)行 Coverage,最終報(bào)告結(jié)果如下:
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.這個(gè)報(bào)告指出我們?nèi)杂?2 個(gè)文件沒(méi)有達(dá)到 100% 的覆蓋率,我們要做的就是為這兩個(gè)文件中未測(cè)試的代碼增加單元測(cè)試,讓其達(dá)到 100% 測(cè)試覆蓋率。
不過(guò)在動(dòng)手寫測(cè)試之前,我們要搞清楚哪些代碼沒(méi)被測(cè)到。命令行報(bào)告的最后一列指出了未被測(cè)試代碼的行號(hào),但是這樣看著不是很直觀。一種體驗(yàn)更好的方式是生成 HTML 報(bào)告,這樣我們可以直接在 HTML 報(bào)告中查看到未被測(cè)試到的具體代碼。
6. 生成 HTML 報(bào)告
coverage report 命令在命令行生成統(tǒng)計(jì)報(bào)告,而 coverage html 則可以生成 HTML 報(bào)告。
在上一節(jié)的基礎(chǔ)上,運(yùn)行如下命令:
$ pipenv run coverage html運(yùn)行完成后項(xiàng)目根目錄會(huì)多出一個(gè) htmlcov 的文件夾,里面就是測(cè)試覆蓋率的 HTML 報(bào)告文件。用瀏覽器打開里面的 index.html 文件就可以查看報(bào)告結(jié)果了:
主頁(yè)和命令行的結(jié)果是一樣的,不過(guò)我們可以點(diǎn)擊文件名,進(jìn)入到對(duì)這個(gè)文件更加具體的統(tǒng)計(jì)報(bào)告頁(yè)面,例如 blog\views.py 結(jié)果如下:
綠色部分代表已覆蓋的代碼,紅色部分代表未覆蓋的代碼。查看文件我們發(fā)現(xiàn),blog\views.py 中未被覆蓋的代碼原來(lái)是 Django 博客實(shí)現(xiàn)簡(jiǎn)單的全文搜索 中的代碼,現(xiàn)在我們已經(jīng)將搜索替換為 Django Haystack 全文檢索 了,這段代碼也就不需要了,可以直接刪除。
7. 完善單元測(cè)試
blog\utils.py 的報(bào)告結(jié)果則表明我們?cè)?Django Haystack 全文檢索與關(guān)鍵詞高亮 中自定義的搜索關(guān)鍵詞高亮器有一個(gè) if 分支條件未被測(cè)試到:
檢查 blog/tests/test_utils.py 中的測(cè)試用例,我們發(fā)現(xiàn)只測(cè)試了比較短的標(biāo)題不被截?cái)?#xff0c;也就是
判斷條件為 True,缺失對(duì)判斷條件為 False 的測(cè)試。所以我們來(lái)構(gòu)造一個(gè)新的測(cè)試用例測(cè)試標(biāo)題長(zhǎng)度超過(guò) max_length (默認(rèn)值為 200)的情況時(shí)會(huì)被截?cái)?#xff1a;
class HighlighterTestCase(TestCase):def test_highlight(self):# 省略已有代碼 ...highlighter = Highlighter("標(biāo)題")document = "這是一個(gè)長(zhǎng)度超過(guò) 200 的標(biāo)題,應(yīng)該被截?cái)唷?#34; + "HelloDjangoTutorial" * 200self.assertTrue(highlighter.highlight(document).startswith('...<span class="highlighted">標(biāo)題</span>,應(yīng)該被截?cái)唷?#39;))再次運(yùn)行 Coverage 生成報(bào)告,測(cè)試覆蓋率全都 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%最后提醒一點(diǎn),Coverage 運(yùn)行后可能會(huì)在項(xiàng)目目錄下生成一些文件,這些文件并不需要納入版本管理,所以將其加入 .gitignore 文件中,防止被提交到代碼庫(kù):
htmlcov/ .coverage .coverage.* coverage.xml *.cover總結(jié)
以上是生活随笔為你收集整理的Diango博客--25.使用Coverage统计测试覆盖率的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: cesium html源码,Cesium
- 下一篇: 【笔试记录】2021/3/13美团