用 Hypothesis 快速測試你的 Python 代碼
介紹
無論你使用哪種編程語言或框架,測試都非常重要。Hypothesis是 Python 的一個高級測試庫。它允許編寫測試用例時參數(shù)化,然后生成使測試失敗的簡單易懂的測試數(shù)據(jù)。可以用更少的工作在代碼中發(fā)現(xiàn)更多的bug。該測試庫覆蓋了大多數(shù)情況,并且確實可以幫助你查找代碼中的錯誤。
這篇文章為展示了如何使用Hypothesis在Python中進(jìn)行測試,并提供了一些示例。
我們?nèi)绾螀^(qū)分測試?
在我們開始進(jìn)行基于屬性的測試之前,我們需要知道測試的一般區(qū)別。有不同的分組測試方法,兩種最常見的方法基于測試方法和測試級別。讓我們從大多數(shù)人已經(jīng)聽說的測試級別開始。本質(zhì)上,存在四個測試級別(盡管人們可能也知道或定義其他級別):
單元測試 集成測試 系統(tǒng)測試 端到端測試
不同測試級別側(cè)重專注于不同的事物。單元測試側(cè)重于軟件的特定部分或功能。這可以是單個功能或功能的一部分。相反,集成測試側(cè)重于通過軟件組件的接口進(jìn)行協(xié)作。系統(tǒng)測試甚至更進(jìn)一步,可以測試整個系統(tǒng)。
現(xiàn)在,我們將看看存在的各種各樣的測試方法。
最常見和已知的是靜態(tài)和動態(tài)測試。所謂靜態(tài)測試(static testing)就是不實際運(yùn)行被測軟件,而只是靜態(tài)地檢查程序代碼、界面或文檔中可能存在的錯誤的過程。如果軟件或其部分實際執(zhí)行,我們稱之為動態(tài)測試。編寫單元測試和集成測試屬于動態(tài)測試。
另一種常見的方法是盒式方法。基本上,它可以分為白盒測試和黑盒測試(以及灰盒測試作為兩者的混合)。白盒測試可驗證程序的內(nèi)部結(jié)構(gòu)或工作情況。黑盒測試與之相反,在黑盒測試中,應(yīng)用程序被視為黑盒,并且對其交互進(jìn)行測試。這意味著在不了解內(nèi)部實現(xiàn)的情況下測試功能。
什么是基于屬性的測試?
現(xiàn)在,我們快速了解了如何區(qū)分測試,您可能會問:什么是基于屬性的測試?
基于屬性的測試技術(shù)( Property-based testing),是指編寫對你的代碼來說為真的邏輯語句(即“屬性”),然后使用自動化工具來生成測試輸入(一般來說,是指某種特定類型的隨機(jī)生成輸入數(shù)據(jù)),并觀察程序接受該輸入時屬性是否保持不變。如果某個輸入違反了某一條屬性,則用戶證明程序存在一處錯誤,并找到一個能夠演示該錯誤的便捷示例。
使用Hypothesis進(jìn)行基于屬性的測試
讓我們舉一個簡單的例子。假設(shè)您有兩個函數(shù)crement()和decrement()。一個示例實現(xiàn)可能如下所示:
#?increment_decrement.py
def?increment(number:?int)?->?int:
????return?number?+?1
def?decrement(number:?int)?->?int:
????return?number?-?1
您可能會為兩者編寫單元測試代碼,如下所示:
#?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
注意:測試代碼是使用pytest框架編寫的。
當(dāng)然,您可以編寫更多的測試腳本來測試具有不同值的兩個函數(shù),甚至可以對測試進(jìn)行參數(shù)化。但是,最后您將使用預(yù)定義的值來測試這兩個功能。
使用基于屬性的測試庫(例如Hypothesis )編寫測試是不同的。在這里,您可以指定要測試的類型以及軟件的工作方式或行為方式。然后該庫根據(jù)指定的類型生成隨機(jī)值來進(jìn)行實際測試功能。
讓我們看看如何使用Hypothesis來測試我們的兩個功能。
#?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
如您所見,這兩個測試腳本都有一個參數(shù)x。x的值是由Hypothesis使用integers()方法生成的。Hypothesis提供了各種方法。本質(zhì)上,這些方法對應(yīng)于內(nèi)置類型或其他結(jié)構(gòu),并生成與給定類型匹配的隨機(jī)數(shù)據(jù)。
聽起來不錯,不是嗎?但是,如果我們想測試具有特定值的函數(shù)以確保它也可以使用該值怎么辦?Hypothesis提供了一個@example()裝飾器,您可以在其中定義一個值,即使該值不屬于隨機(jī)生成的測試數(shù)據(jù)集,也可以將該值傳遞給相應(yīng)的函數(shù)。
讓我們舉個簡單的例子:
#?div.py
def?div(dividend:?int,?divisor:?int)?->?int:
????return?dividend?//?divisor
我們定義了一個函數(shù)div(),該函數(shù)接受一個除數(shù)和一個被除數(shù)并返回兩者的商。請注意,這兩個參數(shù)都是整型數(shù)據(jù),因此結(jié)果也應(yīng)該是整型數(shù)據(jù),我們使用Python的//運(yùn)算符執(zhí)行整數(shù)除法。
為了測試div()函數(shù),我們創(chuàng)建了一個新的測試文件test_div.py并編寫了一個名為test_div()的測試腳本。
#?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ù)的值。我們編寫的測試腳本可能通過也可能不會通過,具體取決于執(zhí)行時Hypothesis產(chǎn)生的值。為了確保始終將值0傳遞給div()函數(shù),我們將@example(1,0)添加到test_div()函數(shù)。因此,即使div()不在隨機(jī)生成的數(shù)據(jù)集中,也至少會用除數(shù)的值0調(diào)用一次。
如果我們按原樣運(yùn)行測試腳本,則test_div()將始終失敗。因此,讓我們修改div()函數(shù)來處理這種情況并使測試通過:
#?div.py
def?div(dividend:?int,?divisor:?int)?->?int:
????if?divisor?==?0:
????????return?-1
????return?dividend?//?divisor
概要
本文主要講了什么是基于屬性的測試以及為什么有用。此外,您快速瀏覽了Hypothesis庫,該庫使您可以編寫基于屬性的測試并與pytest測試一起執(zhí)行。
戀習(xí)Python 關(guān)注戀習(xí)Python,Python都好練 好文章,我在看??
