基於屬性的測試技術( Property-based testing),是指編寫對你的程式碼來說為真的邏輯語句(即“屬性”),然後使用自動化工具來生成測試輸入(一般來說,是指某種特定型別的隨機生成輸入資料),並觀察程式接受該輸入時屬性是否保持不變。如果某個輸入違反了某一條屬性,則使用者證明程式存在一處錯誤,並找到一個能夠演示該錯誤的便捷示例。
基於屬性的測試技術的一個經典示例是測試一個sort(排序)函式,具體程式碼如下所示。
@given(st.lists(st.integers()))
def test_sort(s):
out = list(sorted(s))
assert set(out) == set(s)
assert all(x<=y for x,y in zip(out, out[1:]))
這個測試過程假定,給定一列整數,對其進行排序 - 保持元素的集合不變 - 生成一個有序輸出
隨後,測試框架將針對一些輸入序列的集合自動化執行上述程式碼,並報告是否發現反例。
模糊測試是一種由來已久的實踐技術,它通常是指向程式傳遞某些種類的隨機生成資料(通常是純隨機的位元組流,但可能以某種智慧的方式對其進行了篩選過濾),期望發現能夠引發崩潰的某種輸入(因此,也同樣能夠演示該錯誤)。
近年來,很大程度上由AFL軟體所引領的潮流是,以覆蓋範圍為指導的模糊測試實用技術,採用程式碼插樁/覆蓋的形式,來研究那些更有可能產生有趣行為的輸入;這種技術業已證明對大部分模糊測試目標是非常有效的。
在過去,模糊測試和基於屬性的測試已知被認為是完全不同的兩種技術。基於屬性的測試主要起源於哈斯克爾快速審計(Haskell’s QuickCheck),因此通常與富型別語言、形式規約以及其他相關領域聯絡到一起;而另一方面,模糊測試則通常針對C/C++所編寫的二進位制程式進行測試,一般與安全範疇聯絡在一起——該技術的目標是發現可利用的記憶體破壞錯誤。
然而在本文中,我想要論證的觀點是,模糊測試和基於屬性的測試基本上是同一種技術,至少在某種抽象層面上來說是這樣的。我希望,對這種相似性的識別能夠幫助每一位從業者改進他們的工具和工作流程。
基於屬性的測試Fuzzing
如果我們回退到大約一個抽象層面來看,基於屬性的測試和Fuzzing顯得非常相似。對兩種技術而言,我們都需要:
一次基於屬性的測試過程的傳統粒度是一個函式,而對於Fuzzer來說是一個二進位制程式;但這兩者只是“某些任意計算”的不同實現罷了。
基於屬性的測試需要我們編寫一條屬性作為顯式程式碼,而模糊測試只針對屬性“不崩潰”進行測試。然而,通過簡單正規化“assert(property())”,我們可以將與不崩潰相關的任何屬性轉換為一句斷言;測試人員已經使用該技術發現了非常巧妙的程式行為錯誤。
快速審計,以及許多衍生的基於屬性的測試套件,都使用型別驅動的生成策略,而模糊測試主要使用的是隨機位元組流、人工編碼生成器或者已知良性輸入的隨機變種策略。然而,基本上所有這些方法都只是用於自動化生成輸入資料的策略,測試人員期望這些輸入資料能夠觸發違反測試系統所宣告屬性的行為。
在兩種技術在實踐和工具方面的用法有很多不同;然而很明顯,兩者同樣存在著深度的相似性,而且兩者並不存在根本性的不同。
關注的原因
Fuzzing和基於屬性的測試都有悠久的發展歷史,多樣的工具生態圈以及使用者愛好者社群。然而在我的印象中,兩者相對很少重疊,而且在兩者的生態圈之間沒有大範圍的跨界交流。我認為這是不對的,兩者的工具應該更加緊密地靠攏。在下一篇文章中,我希望針對一些特定技術更加深入地研究其細節,並且希望使用這些技術實現一些基於屬性的測試工具。
附錄:Hypothesis工具介紹
Hypothesis是一款開源的基於屬性的測試工具,主要使用Python語言實現。我認為,該工具在很多方面領先於世界上其他任何一款工具。然而從本文的主題來說,該工具的作者在我之前就意識到了Fuzzing和基於屬性的測試之間的根本相似性,並且已經對其進行了撰文論述,同時在這款工具中引入了很多模糊測試領域的思想。
如果你擁有Python程式設計基礎,那麼你應該學習使用Hypothesis;如果沒有,那麼你應該學習理解Hypothesis,這樣你就可以在自己的程式碼中借用其中最佳的思想。
原文地址:https://blog.nelhage.com/post/property-testing-is-fuzzing/
本文由 看雪翻譯小組 木無聊偶 編譯
轉載請註明來源