摘要:每個測試方法的名稱以單詞開頭,單元測試是如何識別它們是測試的。它還意味著知道測試文件中有多少單元測試,而不是簡單地知道有多少表達式您可能已經注意到將每個行作為多帶帶的測試計數。
來源 | 愿碼(ChainDesk.CN)內容編輯
愿碼Slogan | 連接每個程序員的故事
網站 | http://chaindesk.cn
愿碼愿景 | 打造全學科IT系統免費課程,助力小白用戶、初級工程師0成本免費系統學習、低成本進階,幫助BAT一線資深工程師成長并利用自身優勢創造睡后收入。
官方公眾號 | 愿碼 | 愿碼服務號 | 區塊鏈部落
免費加入愿碼全思維工程師社群 | 任一公眾號回復“愿碼”兩個字獲取入群二維碼
本文閱讀時長:11min
基本單元測試在我們開始討論新的概念和功能之前,讓我們來看看如何使用unittest來表達我們已經學到的想法。這樣,我們就能有一些堅實的基礎來建立我們的新理解。
采取行動的時間-用unittest測試PID我們將訪問PID類(或至少訪問PID類的測試)。我們將編寫測試,以便它們在unittest框架內運行。
我們將使用unittest框架實現測試。
創建一個名為新文件test_pid.py在同一目錄pid.py。請注意,這是一個.py文件:unittest測試是純 python源代碼,而不是包含源代碼的純文本。這意味著從紀錄片的角度來看,測試的用處不大,但可以交換其他好處。
將以下代碼插入到新創建的test_pid.py中
from unittest import TestCase, main from mocker import Mocker import pid class test_pid_constructor(TestCase): def test_without_when(self): mocker = Mocker() mock_time = mocker.replace("time.time") mock_time() mocker.result(1.0) mocker.replay() controller = pid.PID(P=0.5, I=0.5, D=0.5, setpoint=0, initial=12) mocker.restore() mocker.verify() self.assertEqual(controller.gains, (0.5, 0.5, 0.5)) self.assertAlmostEqual(controller.setpoint[0], 0.0) self.assertEqual(len(controller.setpoint), 1) self.assertAlmostEqual(controller.previous_time, 1.0) self.assertAlmostEqual(controller.previous_error, -12.0) self.assertAlmostEqual(controller.integrated_error, 0) def test_with_when(self): controller = pid.PID(P=0.5, I=0.5, D=0.5, setpoint=1, initial=12, when=43) self.assertEqual(controller.gains, (0.5, 0.5, 0.5)) self.assertAlmostEqual(controller.setpoint[0], 1.0) self.assertEqual(len(controller.setpoint), 1) self.assertAlmostEqual(controller.previous_time, 43.0) self.assertAlmostEqual(controller.previous_error, -11.0) self.assertAlmostEqual(controller.integrated_error, 0) class test_calculate_response(TestCase): def test_without_when(self): mocker = Mocker() mock_time = mocker.replace("time.time") mock_time() mocker.result(1.0) mock_time() mocker.result(2.0) mock_time() mocker.result(3.0) mock_time() mocker.result(4.0) mock_time() mocker.result(5.0) mocker.replay() controller = pid.PID(P=0.5, I=0.5, D=0.5, setpoint=0, initial=12) self.assertEqual(controller.calculate_response(6), -3) self.assertEqual(controller.calculate_response(3), -4.5) self.assertEqual(controller.calculate_response(-1.5), -0.75) self.assertEqual(controller.calculate_response(?2.25), ?1.125) mocker.restore() mocker.verify() def test_with_when(self): controller = pid.PID(P=0.5, I=0.5, D=0.5, setpoint=0, initial=12, when=1) self.assertEqual(controller.calculate_response(6, 2), -3) self.assertEqual(controller.calculate_response(3, 3), -4.5) self.assertEqual(controller.calculate_response(?1.5, 4), ?0.75) self.assertEqual(controller.calculate_response(?2.25, 5), ?1.125) if __name__ == "__main__": main()
鍵入以下命令運行測試:$ python test_pid.py
讓我們瀏覽代碼部分,看看每個部分的作用。
from unittest import TestCase, main from mocker import Mocker import pid class test_pid_constructor(TestCase): def test_without_when(self): mocker = Mocker() mock_time = mocker.replace("time.time") mock_time() mocker.result(1.0) mocker.replay() controller = pid.PID(P=0.5, I=0.5, D=0.5, setpoint=0, initial=12) mocker.restore() mocker.verify() self.assertEqual(controller.gains, (0.5, 0.5, 0.5)) self.assertAlmostEqual(controller.setpoint[0], 0.0) self.assertEqual(len(controller.setpoint), 1) self.assertAlmostEqual(controller.previous_time, 1.0) self.assertAlmostEqual(controller.previous_error, -12.0) self.assertAlmostEqual(controller.integrated_error, 0)
在一些設置代碼之后,我們進行了測試,當沒有給出when參數時,PID控制器正常工作。Mocker用于將time.time替換為始終返回可預測值的模擬,然后我們使用多個斷言來確認控制器的屬性已初始化為預期值。
def test_with_when(self): controller = pid.PID(P=0.5, I=0.5, D=0.5, setpoint=1, initial=12, when=43) self.assertEqual(controller.gains, (0.5, 0.5, 0.5)) self.assertAlmostEqual(controller.setpoint[0], 1.0) self.assertEqual(len(controller.setpoint), 1) self.assertAlmostEqual(controller.previous_time, 43.0) self.assertAlmostEqual(controller.previous_error, -11.0) self.assertAlmostEqual(controller.integrated_error, 0)
此測試確認在提供when參數時PID構造函數正常工作。與之前的測試不同,不需要使用Mocker,因為測試的結果不應該依賴于除參數值之外的任何東西 - 當前時間是無關緊要的。
class test_calculate_response(TestCase): def test_without_when(self): mocker = Mocker() mock_time = mocker.replace("time.time") mock_time() mocker.result(1.0) mock_time() mocker.result(2.0) mock_time() mocker.result(3.0) mock_time() mocker.result(4.0) mock_time() mocker.result(5.0) mocker.replay() controller = pid.PID(P=0.5, I=0.5, D=0.5, setpoint=0, initial=12) self.assertEqual(controller.calculate_response(6), -3) self.assertEqual(controller.calculate_response(3), -4.5) self.assertEqual(controller.calculate_response(-1.5), -0.75) sel+f.assertEqual(controller.calculate_response(?2.25), ?1.125) mocker.restore() mocker.verify()
此類中的測試描述了calculate_response方法的預期行為。第一個測試檢查未提供可選的when參數時的行為,并模擬time.time以使該行為可預測。
def test_with_when(self): controller = pid.PID(P=0.5, I=0.5, D=0.5, setpoint=0, initial=12, when=1) self.assertEqual(controller.calculate_response(6, 2), -3) self.assertEqual(controller.calculate_response(3, 3), -4.5) self.assertEqual(controller.calculate_response(?1.5, 4), ?0.75) self.assertEqual(controller.calculate_response(?2.25, 5), ?1.125)
在此測試中,提供了when參數,因此無需模擬time.time。我們只需檢查結果是否符合預期。
我們執行的實際測試與doctest中編寫的測試相同。到目前為止,我們所看到的只是一種表達它們的不同方式。
首先要注意的是,測試文件被劃分為繼承自unittest.TestCase的類,每個類都包含一個或多個測試方法。每個測試方法的名稱以單詞test開頭,單元測試是如何識別它們是測試的。
每種測試方法都包含對單個單元的單個測試。這為我們提供了一種方便的方法來構建我們的測試,將相關測試組合到同一個類中,以便更容易找到它們。
將每個測試放入自己的方法意味著每個測試都在一個獨立的命名空間中執行,這使得相對于doctest風格的測試,使得單元測試式測試更容易相互干擾。它還意味著unittest知道測試文件中有多少單元測試,而不是簡單地知道有多少表達式(您可能已經注意到doctest將每個>>>行作為多帶帶的測試計數)。最后,將每個測試放在自己的方法中意味著每個測試都有一個名稱,這可能是一個有價值的功能。
unittest中的測試并不直接關注任何不屬于調用TestCase的assert方法的任何內容。這意味著當我們使用Mocker時,我們不必擔心從演示表達式返回的模擬對象,除非我們想要使用它們。這也意味著我們需要記住寫一個斷言來描述我們想要檢查的測試的每個方面。我們將很快介紹TestCase的各種斷言方法。
如果您無法執行測試,則測試沒有多大用處。目前,我們將采用的方式是通過Python解釋器將測試文件作為程序執行時 調用unittest.main。這是運行unittest代碼的最簡單方法,但是當你在很多文件中分布了大量測試時,這很麻煩。
如果__name__ =="__ main__":當 Python加載任何模塊時,它將該模塊的名稱存儲在模塊中名為__name__的變量中(除非該模塊是在命令行上傳遞給解釋器的模塊)。該模塊始終將字符串"__main__"綁定到其__name__變量。因此,如果__name__ =="__ main__":表示 - 如果此模塊直接從命令行執行。
AssertionsAssertions是我們用來告訴unittest測試的重要結果是什么的機制。通過使用適當的斷言,我們可以準確地告訴unittest每次測試的期望。
assertTrue當我們調用self.assertTrue(expression)時,我們告訴unittest表達式必須為true才能使測試成功。
這是一個非常靈活的斷言,因為您可以通過編寫適當的布爾表達式來檢查幾乎任何內容。這也是你應該考慮使用的最后一個斷言之一,因為它沒有告訴unittest你正在進行的比較的類型,這意味著unittest無法清楚地告訴你如果測試失敗會出現什么問題。
有關此示例,請考慮以下測試代碼,其中包含兩個保證失敗的測試:
from unittest import TestCase, main class two_failing_tests(TestCase): def test_assertTrue(self): self.assertTrue(1 == 1 + 1) def test_assertEqual(self): self.assertEqual(1, 1 + 1) if __name__ == "__main__": main()
看起來兩個測試似乎是可以互換的,因為兩個測試都是相同的。當然他們都會失敗(或者在不太可能的情況下,他們都會失敗),所以為什么選擇一個而不是另一個呢?
看看我們運行測試時會發生什么(并且還注意到測試沒有按照它們編寫的順序執行;測試完全相互獨立,所以沒關系,對吧?):
你看得到差別嗎?該assertTrue測試能夠正確地確定測試失敗,但它不知道夠報告關于失敗原因的任何有用的信息。該assertEqual便測試,而另一方面,他知道首先,它是檢查兩個表達式是相等的,其次它知道如何呈現的結果,因此,他們將是最有用的:通過評估各個它是表達的比較并在結果之間放置一個!=符號。它告訴我們什么期望失敗,以及相關表達式評估的內容。
assertFalse該assertFalse方法會成功時assertTrue方法會失敗,反之亦然。它在產生assertTrue所具有的有用輸出方面具有相同的限制,并且在能夠測試幾乎任何條件方面具有相同的靈活性。
assertEqual正如assertTrue討論中所提到的,assertEqual斷言檢查它的兩個參數實際上是相等的,并且如果它們不是,則報告失敗,以及參數的實際值。
assertNotEqual該assertNotEqual每當斷言失敗assertEqual便斷言會成功,反之亦然。報告失敗時,其輸出表明兩個表達式的值相等,并為您提供這些值。
assertAlmostEqual正如我們之前看到的,比較浮點數可能很麻煩。特別是,檢查兩個浮點數是否相等是有問題的,因為你可能期望相等的事情 - 在數學上是相等的 - 可能仍然最終在最低有效位之間不同。浮點數僅在每個位相同時才相等。
為了解決這個問題,unittest提供了assertAlmostEqual,它檢查兩個浮點值是否幾乎相同; 它們之間的少量差異是可以容忍的。
讓我們看一下這個問題。如果取平方根7,然后將其平方,則結果應為7.這是一對檢查該事實的測試:
from unittest import TestCase, main class floating_point_problems(TestCase): def test_assertEqual(self): self.assertEqual((7.0 ** 0.5) ** 2.0, 7.0) def test_assertAlmostEqual(self): self.assertAlmostEqual((7.0 ** 0.5) ** 2.0, 7.0) if __name__ == "__main__": main()
該test_assertEqual方法檢查
這在現實中是如此。然而,在計算機可用的更專業的數字系統中,取7的平方根然后平方它并不能讓我們回到7,所以這個測試將失敗。稍等一下。
測試test_assertAlmostEqual方法檢查
即使計算機會同意這是真的,所以這個測試應該通過。
運行這些測試會產生以下結果,盡管您返回的具體數字可能會有所不同,具體取決于運行測試的計算機的詳細信息:
不幸的是,浮點數不精確,因為實數行上的大多數數字不能用有限的,非重復的數字序列表示,更不用說僅僅64位。因此,你從評估數學表達式得到的回報并不是很好。雖然 - 或者幾乎任何其他類型的工作都足夠接近政府工作 - 所以我們不希望我們的測試對這個微小的差異進行狡辯。因此,當我們比較浮點數是否相等時,我們應該使用assertAlmostEqual和assertNotAlmostEqual。
這個問題通常不會延續到其他比較運算符中。例如,檢查一個浮點數小于另一個,由于無意義的錯誤,不太可能產生錯誤的結果。只有在平等的情況下,這個問題才會困擾我們。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/43760.html
摘要:必然的,他們會拋棄標準庫中的,使用或者發明自己心儀的單元測試框架。究其原因,一些人會說時間寫代碼都不夠,哪還有空寫單元測試。最后我的個人觀點,單元測試其實還有一個非常重要的作用,就是替代函數文檔注釋。希望從今天起,你的代碼也都有單元測試。 單元測試是每種編程語言必學的課題,是保護開發者的強力護盾,每個程序員都在時間允許的情況下盡可能多的寫單元測試,今天我們不討論其必要性,只拋磚引玉聊一...
摘要:本文將進入單元測試的部分,這也是基礎知識中最后一個大塊。本文將重點講述和中的單元測試的生態環境。另外,在中指定要運行的單元測試用例的完整語法是。中使用模塊管理單元測試用例。每個項目的單元測試代碼結構可 本文將進入單元測試的部分,這也是基礎知識中最后一個大塊。本文將重點講述Python和OpenStack中的單元測試的生態環境。 單元測試的重要性 github上有個人畫了一些不同語言的學...
摘要:所謂的單元測試,就是對一個模塊,一個函數,或則是一個類進行正確性檢測的一類測試工作。當然,單元測試也會讓代碼量大大增加。編寫單元測試代碼需要引入的包。再所有單元測試開始前運行函數在所有單元測試運行后運行。 所謂的單元測試,就是對一個模塊,一個函數,或則是一個類進行正確性檢測的一類測試工作。 以測試驅動的開發方式叫做測試驅動開發(Test Drived Development). 這種開...
摘要:單元測試框架作為的標準庫,是其他單元測試框架的基礎。可以和和配合使用編寫單元測試。官網地址單元測試覆蓋率工具單元測試中還需要用到代碼覆蓋率工具。代碼覆蓋率統計工具用來發現沒有被測試覆蓋的代碼,完善單元測試的覆蓋率。 在應用程序中,單元是具有一個或多個輸入和單個輸出的軟件中最小可測試部分。單元...
小編這這篇文章的主要目的,主要是給大家進行一個詳解,解釋一下關于Python中,單元格測試的一些具體方法,那么,測試的方法都有什么呢?下面小編就給大家詳細的做出一個解答。 一、前言 python的兩個單元測試包分別是doctest和unittest,這兩個包的使用起來各有長處,適用于不同的場景 doctest:直接寫在方法體中,利用了python動態語言的特性,書寫方式簡單明了,前提是項...
閱讀 1090·2021-11-15 18:00
閱讀 2802·2021-09-22 15:18
閱讀 1964·2021-09-04 16:45
閱讀 750·2019-08-30 15:55
閱讀 3852·2019-08-30 13:10
閱讀 1331·2019-08-30 11:06
閱讀 1983·2019-08-29 12:51
閱讀 2294·2019-08-26 13:55