摘要:每個(gè)測(cè)試方法的名稱以單詞開頭,單元測(cè)試是如何識(shí)別它們是測(cè)試的。它還意味著知道測(cè)試文件中有多少單元測(cè)試,而不是簡(jiǎn)單地知道有多少表達(dá)式您可能已經(jīng)注意到將每個(gè)行作為多帶帶的測(cè)試計(jì)數(shù)。
來源 | 愿碼(ChainDesk.CN)內(nèi)容編輯
愿碼Slogan | 連接每個(gè)程序員的故事
網(wǎng)站 | http://chaindesk.cn
愿碼愿景 | 打造全學(xué)科IT系統(tǒng)免費(fèi)課程,助力小白用戶、初級(jí)工程師0成本免費(fèi)系統(tǒng)學(xué)習(xí)、低成本進(jìn)階,幫助BAT一線資深工程師成長(zhǎng)并利用自身優(yōu)勢(shì)創(chuàng)造睡后收入。
官方公眾號(hào) | 愿碼 | 愿碼服務(wù)號(hào) | 區(qū)塊鏈部落
免費(fèi)加入愿碼全思維工程師社群 | 任一公眾號(hào)回復(fù)“愿碼”兩個(gè)字獲取入群二維碼
本文閱讀時(shí)長(zhǎng):11min
基本單元測(cè)試在我們開始討論新的概念和功能之前,讓我們來看看如何使用unittest來表達(dá)我們已經(jīng)學(xué)到的想法。這樣,我們就能有一些堅(jiān)實(shí)的基礎(chǔ)來建立我們的新理解。
采取行動(dòng)的時(shí)間-用unittest測(cè)試PID我們將訪問PID類(或至少訪問PID類的測(cè)試)。我們將編寫測(cè)試,以便它們?cè)趗nittest框架內(nèi)運(yùn)行。
我們將使用unittest框架實(shí)現(xiàn)測(cè)試。
創(chuàng)建一個(gè)名為新文件test_pid.py在同一目錄pid.py。請(qǐng)注意,這是一個(gè).py文件:unittest測(cè)試是純 python源代碼,而不是包含源代碼的純文本。這意味著從紀(jì)錄片的角度來看,測(cè)試的用處不大,但可以交換其他好處。
將以下代碼插入到新創(chuàng)建的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()
鍵入以下命令運(yùn)行測(cè)試:$ python test_pid.py
讓我們?yōu)g覽代碼部分,看看每個(gè)部分的作用。
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)
在一些設(shè)置代碼之后,我們進(jìn)行了測(cè)試,當(dāng)沒有給出when參數(shù)時(shí),PID控制器正常工作。Mocker用于將time.time替換為始終返回可預(yù)測(cè)值的模擬,然后我們使用多個(gè)斷言來確認(rèn)控制器的屬性已初始化為預(yù)期值。
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)
此測(cè)試確認(rèn)在提供when參數(shù)時(shí)PID構(gòu)造函數(shù)正常工作。與之前的測(cè)試不同,不需要使用Mocker,因?yàn)闇y(cè)試的結(jié)果不應(yīng)該依賴于除參數(shù)值之外的任何東西 - 當(dāng)前時(shí)間是無關(guān)緊要的。
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()
此類中的測(cè)試描述了calculate_response方法的預(yù)期行為。第一個(gè)測(cè)試檢查未提供可選的when參數(shù)時(shí)的行為,并模擬time.time以使該行為可預(yù)測(cè)。
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)
在此測(cè)試中,提供了when參數(shù),因此無需模擬time.time。我們只需檢查結(jié)果是否符合預(yù)期。
我們執(zhí)行的實(shí)際測(cè)試與doctest中編寫的測(cè)試相同。到目前為止,我們所看到的只是一種表達(dá)它們的不同方式。
首先要注意的是,測(cè)試文件被劃分為繼承自unittest.TestCase的類,每個(gè)類都包含一個(gè)或多個(gè)測(cè)試方法。每個(gè)測(cè)試方法的名稱以單詞test開頭,單元測(cè)試是如何識(shí)別它們是測(cè)試的。
每種測(cè)試方法都包含對(duì)單個(gè)單元的單個(gè)測(cè)試。這為我們提供了一種方便的方法來構(gòu)建我們的測(cè)試,將相關(guān)測(cè)試組合到同一個(gè)類中,以便更容易找到它們。
將每個(gè)測(cè)試放入自己的方法意味著每個(gè)測(cè)試都在一個(gè)獨(dú)立的命名空間中執(zhí)行,這使得相對(duì)于doctest風(fēng)格的測(cè)試,使得單元測(cè)試式測(cè)試更容易相互干擾。它還意味著unittest知道測(cè)試文件中有多少單元測(cè)試,而不是簡(jiǎn)單地知道有多少表達(dá)式(您可能已經(jīng)注意到doctest將每個(gè)>>>行作為多帶帶的測(cè)試計(jì)數(shù))。最后,將每個(gè)測(cè)試放在自己的方法中意味著每個(gè)測(cè)試都有一個(gè)名稱,這可能是一個(gè)有價(jià)值的功能。
unittest中的測(cè)試并不直接關(guān)注任何不屬于調(diào)用TestCase的assert方法的任何內(nèi)容。這意味著當(dāng)我們使用Mocker時(shí),我們不必?fù)?dān)心從演示表達(dá)式返回的模擬對(duì)象,除非我們想要使用它們。這也意味著我們需要記住寫一個(gè)斷言來描述我們想要檢查的測(cè)試的每個(gè)方面。我們將很快介紹TestCase的各種斷言方法。
如果您無法執(zhí)行測(cè)試,則測(cè)試沒有多大用處。目前,我們將采用的方式是通過Python解釋器將測(cè)試文件作為程序執(zhí)行時(shí) 調(diào)用unittest.main。這是運(yùn)行unittest代碼的最簡(jiǎn)單方法,但是當(dāng)你在很多文件中分布了大量測(cè)試時(shí),這很麻煩。
如果__name__ =="__ main__":當(dāng) Python加載任何模塊時(shí),它將該模塊的名稱存儲(chǔ)在模塊中名為__name__的變量中(除非該模塊是在命令行上傳遞給解釋器的模塊)。該模塊始終將字符串"__main__"綁定到其__name__變量。因此,如果__name__ =="__ main__":表示 - 如果此模塊直接從命令行執(zhí)行。
AssertionsAssertions是我們用來告訴unittest測(cè)試的重要結(jié)果是什么的機(jī)制。通過使用適當(dāng)?shù)臄嘌裕覀兛梢詼?zhǔn)確地告訴unittest每次測(cè)試的期望。
assertTrue當(dāng)我們調(diào)用self.assertTrue(expression)時(shí),我們告訴unittest表達(dá)式必須為true才能使測(cè)試成功。
這是一個(gè)非常靈活的斷言,因?yàn)槟梢酝ㄟ^編寫適當(dāng)?shù)牟紶柋磉_(dá)式來檢查幾乎任何內(nèi)容。這也是你應(yīng)該考慮使用的最后一個(gè)斷言之一,因?yàn)樗鼪]有告訴unittest你正在進(jìn)行的比較的類型,這意味著unittest無法清楚地告訴你如果測(cè)試失敗會(huì)出現(xiàn)什么問題。
有關(guān)此示例,請(qǐng)考慮以下測(cè)試代碼,其中包含兩個(gè)保證失敗的測(cè)試:
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()
看起來兩個(gè)測(cè)試似乎是可以互換的,因?yàn)閮蓚€(gè)測(cè)試都是相同的。當(dāng)然他們都會(huì)失?。ɑ蛘咴诓惶赡艿那闆r下,他們都會(huì)失?。?,所以為什么選擇一個(gè)而不是另一個(gè)呢?
看看我們運(yùn)行測(cè)試時(shí)會(huì)發(fā)生什么(并且還注意到測(cè)試沒有按照它們編寫的順序執(zhí)行;測(cè)試完全相互獨(dú)立,所以沒關(guān)系,對(duì)吧?):
你看得到差別嗎?該assertTrue測(cè)試能夠正確地確定測(cè)試失敗,但它不知道夠報(bào)告關(guān)于失敗原因的任何有用的信息。該assertEqual便測(cè)試,而另一方面,他知道首先,它是檢查兩個(gè)表達(dá)式是相等的,其次它知道如何呈現(xiàn)的結(jié)果,因此,他們將是最有用的:通過評(píng)估各個(gè)它是表達(dá)的比較并在結(jié)果之間放置一個(gè)!=符號(hào)。它告訴我們什么期望失敗,以及相關(guān)表達(dá)式評(píng)估的內(nèi)容。
assertFalse該assertFalse方法會(huì)成功時(shí)assertTrue方法會(huì)失敗,反之亦然。它在產(chǎn)生assertTrue所具有的有用輸出方面具有相同的限制,并且在能夠測(cè)試幾乎任何條件方面具有相同的靈活性。
assertEqual正如assertTrue討論中所提到的,assertEqual斷言檢查它的兩個(gè)參數(shù)實(shí)際上是相等的,并且如果它們不是,則報(bào)告失敗,以及參數(shù)的實(shí)際值。
assertNotEqual該assertNotEqual每當(dāng)斷言失敗assertEqual便斷言會(huì)成功,反之亦然。報(bào)告失敗時(shí),其輸出表明兩個(gè)表達(dá)式的值相等,并為您提供這些值。
assertAlmostEqual正如我們之前看到的,比較浮點(diǎn)數(shù)可能很麻煩。特別是,檢查兩個(gè)浮點(diǎn)數(shù)是否相等是有問題的,因?yàn)槟憧赡芷谕嗟鹊氖虑?- 在數(shù)學(xué)上是相等的 - 可能仍然最終在最低有效位之間不同。浮點(diǎn)數(shù)僅在每個(gè)位相同時(shí)才相等。
為了解決這個(gè)問題,unittest提供了assertAlmostEqual,它檢查兩個(gè)浮點(diǎn)值是否幾乎相同; 它們之間的少量差異是可以容忍的。
讓我們看一下這個(gè)問題。如果取平方根7,然后將其平方,則結(jié)果應(yīng)為7.這是一對(duì)檢查該事實(shí)的測(cè)試:
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方法檢查
這在現(xiàn)實(shí)中是如此。然而,在計(jì)算機(jī)可用的更專業(yè)的數(shù)字系統(tǒng)中,取7的平方根然后平方它并不能讓我們回到7,所以這個(gè)測(cè)試將失敗。稍等一下。
測(cè)試test_assertAlmostEqual方法檢查
即使計(jì)算機(jī)會(huì)同意這是真的,所以這個(gè)測(cè)試應(yīng)該通過。
運(yùn)行這些測(cè)試會(huì)產(chǎn)生以下結(jié)果,盡管您返回的具體數(shù)字可能會(huì)有所不同,具體取決于運(yùn)行測(cè)試的計(jì)算機(jī)的詳細(xì)信息:
不幸的是,浮點(diǎn)數(shù)不精確,因?yàn)閷?shí)數(shù)行上的大多數(shù)數(shù)字不能用有限的,非重復(fù)的數(shù)字序列表示,更不用說僅僅64位。因此,你從評(píng)估數(shù)學(xué)表達(dá)式得到的回報(bào)并不是很好。雖然 - 或者幾乎任何其他類型的工作都足夠接近政府工作 - 所以我們不希望我們的測(cè)試對(duì)這個(gè)微小的差異進(jìn)行狡辯。因此,當(dāng)我們比較浮點(diǎn)數(shù)是否相等時(shí),我們應(yīng)該使用assertAlmostEqual和assertNotAlmostEqual。
這個(gè)問題通常不會(huì)延續(xù)到其他比較運(yùn)算符中。例如,檢查一個(gè)浮點(diǎn)數(shù)小于另一個(gè),由于無意義的錯(cuò)誤,不太可能產(chǎn)生錯(cuò)誤的結(jié)果。只有在平等的情況下,這個(gè)問題才會(huì)困擾我們。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/43760.html
摘要:必然的,他們會(huì)拋棄標(biāo)準(zhǔn)庫(kù)中的,使用或者發(fā)明自己心儀的單元測(cè)試框架。究其原因,一些人會(huì)說時(shí)間寫代碼都不夠,哪還有空寫單元測(cè)試。最后我的個(gè)人觀點(diǎn),單元測(cè)試其實(shí)還有一個(gè)非常重要的作用,就是替代函數(shù)文檔注釋。希望從今天起,你的代碼也都有單元測(cè)試。 單元測(cè)試是每種編程語(yǔ)言必學(xué)的課題,是保護(hù)開發(fā)者的強(qiáng)力護(hù)盾,每個(gè)程序員都在時(shí)間允許的情況下盡可能多的寫單元測(cè)試,今天我們不討論其必要性,只拋磚引玉聊一...
摘要:本文將進(jìn)入單元測(cè)試的部分,這也是基礎(chǔ)知識(shí)中最后一個(gè)大塊。本文將重點(diǎn)講述和中的單元測(cè)試的生態(tài)環(huán)境。另外,在中指定要運(yùn)行的單元測(cè)試用例的完整語(yǔ)法是。中使用模塊管理單元測(cè)試用例。每個(gè)項(xiàng)目的單元測(cè)試代碼結(jié)構(gòu)可 本文將進(jìn)入單元測(cè)試的部分,這也是基礎(chǔ)知識(shí)中最后一個(gè)大塊。本文將重點(diǎn)講述Python和OpenStack中的單元測(cè)試的生態(tài)環(huán)境。 單元測(cè)試的重要性 github上有個(gè)人畫了一些不同語(yǔ)言的學(xué)...
摘要:所謂的單元測(cè)試,就是對(duì)一個(gè)模塊,一個(gè)函數(shù),或則是一個(gè)類進(jìn)行正確性檢測(cè)的一類測(cè)試工作。當(dāng)然,單元測(cè)試也會(huì)讓代碼量大大增加。編寫單元測(cè)試代碼需要引入的包。再所有單元測(cè)試開始前運(yùn)行函數(shù)在所有單元測(cè)試運(yùn)行后運(yùn)行。 所謂的單元測(cè)試,就是對(duì)一個(gè)模塊,一個(gè)函數(shù),或則是一個(gè)類進(jìn)行正確性檢測(cè)的一類測(cè)試工作。 以測(cè)試驅(qū)動(dòng)的開發(fā)方式叫做測(cè)試驅(qū)動(dòng)開發(fā)(Test Drived Development). 這種開...
摘要:?jiǎn)卧獪y(cè)試框架作為的標(biāo)準(zhǔn)庫(kù),是其他單元測(cè)試框架的基礎(chǔ)??梢院秃团浜鲜褂镁帉憜卧獪y(cè)試。官網(wǎng)地址單元測(cè)試覆蓋率工具單元測(cè)試中還需要用到代碼覆蓋率工具。代碼覆蓋率統(tǒng)計(jì)工具用來發(fā)現(xiàn)沒有被測(cè)試覆蓋的代碼,完善單元測(cè)試的覆蓋率。 在應(yīng)用程序中,單元是具有一個(gè)或多個(gè)輸入和單個(gè)輸出的軟件中最小可測(cè)試部分。單元...
小編這這篇文章的主要目的,主要是給大家進(jìn)行一個(gè)詳解,解釋一下關(guān)于Python中,單元格測(cè)試的一些具體方法,那么,測(cè)試的方法都有什么呢?下面小編就給大家詳細(xì)的做出一個(gè)解答?! ∫弧⑶把浴 ython的兩個(gè)單元測(cè)試包分別是doctest和unittest,這兩個(gè)包的使用起來各有長(zhǎng)處,適用于不同的場(chǎng)景 doctest:直接寫在方法體中,利用了python動(dòng)態(tài)語(yǔ)言的特性,書寫方式簡(jiǎn)單明了,前提是項(xiàng)...
摘要:測(cè)試的通用規(guī)則測(cè)試單元應(yīng)該集中于小部分的功能,并且證明它是對(duì)的。通過去除依賴盡量使測(cè)試單元快速運(yùn)行。實(shí)現(xiàn)來持續(xù)集成通過代碼提交的本地或者來持續(xù)集成測(cè)試你的代碼。 原文鏈接:http://blog.speedx.com/backend-test-guide 將測(cè)試代碼和運(yùn)行代碼一起寫是一種非常好的習(xí)慣。聰明地使用這種方法將會(huì)幫助你更加精確地定義代碼的含義,并且代碼的耦合性更低。 測(cè)試的通...
閱讀 1165·2021-11-15 18:00
閱讀 2968·2021-09-22 15:18
閱讀 2030·2021-09-04 16:45
閱讀 820·2019-08-30 15:55
閱讀 4016·2019-08-30 13:10
閱讀 1436·2019-08-30 11:06
閱讀 2058·2019-08-29 12:51
閱讀 2373·2019-08-26 13:55