用 Hypothesis 快速測(cè)試你的 Python 代碼
點(diǎn)擊藍(lán)色“Python交流圈”關(guān)注我丫
加個(gè)“星標(biāo)”,每天一起進(jìn)步一點(diǎn)點(diǎn)

介紹
無論你使用哪種編程語言或框架,測(cè)試都非常重要。Hypothesis是 Python 的一個(gè)高級(jí)測(cè)試庫。它允許編寫測(cè)試用例時(shí)參數(shù)化,然后生成使測(cè)試失敗的簡(jiǎn)單易懂的測(cè)試數(shù)據(jù)??梢杂酶俚墓ぷ髟诖a中發(fā)現(xiàn)更多的bug。該測(cè)試庫覆蓋了大多數(shù)情況,并且確實(shí)可以幫助你查找代碼中的錯(cuò)誤。
這篇文章為展示了如何使用Hypothesis在Python中進(jìn)行測(cè)試,并提供了一些示例。
我們?nèi)绾螀^(qū)分測(cè)試?
在我們開始進(jìn)行基于屬性的測(cè)試之前,我們需要知道測(cè)試的一般區(qū)別。有不同的分組測(cè)試方法,兩種最常見的方法基于測(cè)試方法和測(cè)試級(jí)別。讓我們從大多數(shù)人已經(jīng)聽說的測(cè)試級(jí)別開始。本質(zhì)上,存在四個(gè)測(cè)試級(jí)別(盡管人們可能也知道或定義其他級(jí)別):
單元測(cè)試 集成測(cè)試 系統(tǒng)測(cè)試 端到端測(cè)試
不同測(cè)試級(jí)別側(cè)重專注于不同的事物。單元測(cè)試側(cè)重于軟件的特定部分或功能。這可以是單個(gè)功能或功能的一部分。相反,集成測(cè)試側(cè)重于通過軟件組件的接口進(jìn)行協(xié)作。系統(tǒng)測(cè)試甚至更進(jìn)一步,可以測(cè)試整個(gè)系統(tǒng)。
現(xiàn)在,我們將看看存在的各種各樣的測(cè)試方法。
最常見和已知的是靜態(tài)和動(dòng)態(tài)測(cè)試。所謂靜態(tài)測(cè)試(static testing)就是不實(shí)際運(yùn)行被測(cè)軟件,而只是靜態(tài)地檢查程序代碼、界面或文檔中可能存在的錯(cuò)誤的過程。如果軟件或其部分實(shí)際執(zhí)行,我們稱之為動(dòng)態(tài)測(cè)試。編寫單元測(cè)試和集成測(cè)試屬于動(dòng)態(tài)測(cè)試。
另一種常見的方法是盒式方法?;旧?,它可以分為白盒測(cè)試和黑盒測(cè)試(以及灰盒測(cè)試作為兩者的混合)。白盒測(cè)試可驗(yàn)證程序的內(nèi)部結(jié)構(gòu)或工作情況。黑盒測(cè)試與之相反,在黑盒測(cè)試中,應(yīng)用程序被視為黑盒,并且對(duì)其交互進(jìn)行測(cè)試。這意味著在不了解內(nèi)部實(shí)現(xiàn)的情況下測(cè)試功能。
什么是基于屬性的測(cè)試?
現(xiàn)在,我們快速了解了如何區(qū)分測(cè)試,您可能會(huì)問:什么是基于屬性的測(cè)試?
基于屬性的測(cè)試技術(shù)( Property-based testing),是指編寫對(duì)你的代碼來說為真的邏輯語句(即“屬性”),然后使用自動(dòng)化工具來生成測(cè)試輸入(一般來說,是指某種特定類型的隨機(jī)生成輸入數(shù)據(jù)),并觀察程序接受該輸入時(shí)屬性是否保持不變。如果某個(gè)輸入違反了某一條屬性,則用戶證明程序存在一處錯(cuò)誤,并找到一個(gè)能夠演示該錯(cuò)誤的便捷示例。
使用Hypothesis進(jìn)行基于屬性的測(cè)試
讓我們舉一個(gè)簡(jiǎn)單的例子。假設(shè)您有兩個(gè)函數(shù)crement()和decrement()。一個(gè)示例實(shí)現(xiàn)可能如下所示:
#?increment_decrement.py
def?increment(number:?int)?->?int:
????return?number?+?1
def?decrement(number:?int)?->?int:
????return?number?-?1
您可能會(huì)為兩者編寫單元測(cè)試代碼,如下所示:
#?test_increment_decrement_pytest.py
from?increment_decrement?import?decrement
from?increment_decrement?import?increment
def?test_increment():
????x?=?5
????expected?=?6
????actual?=?increment(x)
????assert?actual?==?expected
def?test_decrement():
????x?=?5
????expected?=?4
????actual?=?decrement(x)
????assert?actual?==?expected
注意:測(cè)試代碼是使用pytest框架編寫的。
當(dāng)然,您可以編寫更多的測(cè)試腳本來測(cè)試具有不同值的兩個(gè)函數(shù),甚至可以對(duì)測(cè)試進(jìn)行參數(shù)化。但是,最后您將使用預(yù)定義的值來測(cè)試這兩個(gè)功能。
使用基于屬性的測(cè)試庫(例如Hypothesis )編寫測(cè)試是不同的。在這里,您可以指定要測(cè)試的類型以及軟件的工作方式或行為方式。然后該庫根據(jù)指定的類型生成隨機(jī)值來進(jìn)行實(shí)際測(cè)試功能。
讓我們看看如何使用Hypothesis來測(cè)試我們的兩個(gè)功能。
#?test_increment_decrement_hypothesis.py
from?hypothesis?import?given
import?hypothesis.strategies?as?st
from?increment_decrement?import?decrement
from?increment_decrement?import?increment
@given(st.integers())
def?test_increment(x):
????expected?=?x?+?1
????actual?=?increment(x)
????assert?actual?==?expected
@given(st.integers())
def?test_decrement(x):
????expected?=?x?-?1
????actual?=?decrement(x)
????assert?actual?==?expected
如您所見,這兩個(gè)測(cè)試腳本都有一個(gè)參數(shù)x。x的值是由Hypothesis使用integers()方法生成的。Hypothesis提供了各種方法。本質(zhì)上,這些方法對(duì)應(yīng)于內(nèi)置類型或其他結(jié)構(gòu),并生成與給定類型匹配的隨機(jī)數(shù)據(jù)。
聽起來不錯(cuò),不是嗎?但是,如果我們想測(cè)試具有特定值的函數(shù)以確保它也可以使用該值怎么辦?Hypothesis提供了一個(gè)@example()裝飾器,您可以在其中定義一個(gè)值,即使該值不屬于隨機(jī)生成的測(cè)試數(shù)據(jù)集,也可以將該值傳遞給相應(yīng)的函數(shù)。
讓我們舉個(gè)簡(jiǎn)單的例子:
#?div.py
def?div(dividend:?int,?divisor:?int)?->?int:
????return?dividend?//?divisor
我們定義了一個(gè)函數(shù)div(),該函數(shù)接受一個(gè)除數(shù)和一個(gè)被除數(shù)并返回兩者的商。請(qǐng)注意,這兩個(gè)參數(shù)都是整型數(shù)據(jù),因此結(jié)果也應(yīng)該是整型數(shù)據(jù),我們使用Python的//運(yùn)算符執(zhí)行整數(shù)除法。
為了測(cè)試div()函數(shù),我們創(chuàng)建了一個(gè)新的測(cè)試文件test_div.py并編寫了一個(gè)名為test_div()的測(cè)試腳本。
#?test_div.py
from?hypothesis?import?example
from?hypothesis?import?given
import?hypothesis.strategies?as?st
from?div?import?div
@given(dividend=st.integers(),?divisor=st.integers())
def?test_div(dividend,?divisor):
????if?divisor?==?0:
????????expected?=?-1
????else:
????????expected?=?dividend?//?divisor
????actual?=?div(dividend,?divisor)
????assert?actual?==?expected
同樣,我們使用Hypothesis的integers()方法生成除數(shù)和被除數(shù)的值。我們編寫的測(cè)試腳本可能通過也可能不會(huì)通過,具體取決于執(zhí)行時(shí)Hypothesis產(chǎn)生的值。為了確保始終將值0傳遞給div()函數(shù),我們將@example(1,0)添加到test_div()函數(shù)。因此,即使div()不在隨機(jī)生成的數(shù)據(jù)集中,也至少會(huì)用除數(shù)的值0調(diào)用一次。
如果我們按原樣運(yùn)行測(cè)試腳本,則test_div()將始終失敗。因此,讓我們修改div()函數(shù)來處理這種情況并使測(cè)試通過:
#?div.py
def?div(dividend:?int,?divisor:?int)?->?int:
????if?divisor?==?0:
????????return?-1
????return?dividend?//?divisor
概要
本文主要講了什么是基于屬性的測(cè)試以及為什么有用。此外,您快速瀏覽了Hypothesis庫,該庫使您可以編寫基于屬性的測(cè)試并與pytest測(cè)試一起執(zhí)行。
--End--
近期熱門推薦? 1、別這樣直接運(yùn)行Python命令,否則電腦等于“裸奔”
4、厲害了!手?jǐn)]一個(gè)微信訂閱號(hào)鑒黃機(jī)器人!用起來很可以!
5、大廠的 404 頁面都長(zhǎng)啥樣?看到最后一個(gè),我笑了。。。
7、11 月全國(guó)程序員平均工資出爐,看你拉后退了沒
8、用 Python 使用 Google Colab?豈止是炫酷
關(guān)注公眾號(hào),回復(fù)“001” 領(lǐng)取Python入門+進(jìn)階+實(shí)戰(zhàn)開發(fā)92天全套視頻教程
點(diǎn)贊最大的支持?



