本篇文章是介紹的是Python 世界中最火的第三方單元測試框架:pytest。
它有以下主要特性:html
self.assert*
名稱了)unittest
徹底兼容,對 nose
基本兼容和前面介紹 unittest
和 nose
同樣,咱們將從以下幾個方面介紹 pytest
的特性。python
同 nose
同樣,pytest
支持函數、測試類形式的測試用例。最大的不一樣點是,你能夠盡情地使用 assert
語句進行斷言,絲絕不用擔憂它會在 nose
或 unittest
中產生的缺失詳細上下文信息的問題。面試
好比下面的測試示例中,故意使得 test_upper
中斷言不經過:session
import pytest def test_upper(): assert 'foo'.upper() == 'FOO1' class TestClass: def test_one(self): x = "this" assert "h" in x def test_two(self): x = "hello" with pytest.raises(TypeError): x + []
而當使用 pytest
去執行用例時,它會輸出詳細的(且是多種顏色)上下文信息:架構
=================================== test session starts =================================== platform darwin -- Python 3.7.1, pytest-4.0.1, py-1.7.0, pluggy-0.8.0 rootdir: /Users/prodesire/projects/tests, inifile: plugins: cov-2.6.0 collected 3 items test.py F.. [100%] ======================================== FAILURES ========================================= _______________________________________ test_upper ________________________________________ def test_upper(): > assert 'foo'.upper() == 'FOO1' E AssertionError: assert 'FOO' == 'FOO1' E - FOO E + FOO1 E ? + test.py:4: AssertionError =========================== 1 failed, 2 passed in 0.08 seconds ============================
不難看到,pytest
既輸出了測試代碼上下文,也輸出了被測變量值的信息。相比於 nose
和 unittest
,pytest
容許用戶使用更簡單的方式編寫測試用例,又能獲得一個更豐富和友好的測試結果。框架
unittest
和 nose
所支持的用例發現和執行能力,pytest
均支持。 pytest
支持用例自動(遞歸)發現:ide
test_*.py
或 *_test.py
的測試用例文件中,以 test
開頭的測試函數或以 Test
開頭的測試類中的以 test
開頭的測試方法pytest
命令nose2
的理念同樣,經過在配置文件中指定特定參數,可配置用例文件、類和函數的名稱模式(模糊匹配)pytest
也支持執行指定用例:模塊化
pytest /path/to/test/file.py
pytest /path/to/test/file.py:TestCase
pytest another.test::TestClass::test_method
pytest /path/to/test/file.py:test_function
pytest
的測試夾具和 unittest
、nose
、nose2
的風格迥異,它不但能實現 setUp
和 tearDown
這種測試前置和清理邏輯,還其餘很是多強大的功能。函數
pytest
中的測試夾具更像是測試資源,你只需定義一個夾具,而後就能夠在用例中直接使用它。得益於 pytest
的依賴注入機制,你無需經過from xx import xx
的形式顯示導入,只須要在測試函數的參數中指定同名參數便可,好比:佈局
import pytest @pytest.fixture def smtp_connection(): import smtplib return smtplib.SMTP("smtp.gmail.com", 587, timeout=5) def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250
上述示例中定義了一個測試夾具 smtp_connection
,在測試函數 test_ehlo
簽名中定義了同名參數,則 pytest
框架會自動注入該變量。
在 pytest
中,同一個測試夾具可被多個測試文件中的多個測試用例共享。只需在包(Package)中定義 conftest.py
文件,並把測試夾具的定義寫在該文件中,則該包內全部模塊(Module)的全部測試用例都可使用 conftest.py
中所定義的測試夾具。
好比,若是在以下文件結構的 test_1/conftest.py
定義了測試夾具,那麼 test_a.py
和 test_b.py
可使用該測試夾具;而 test_c.py
則沒法使用。
`-- test_1 | |-- conftest.py | `-- test_a.py | `-- test_b.py `-- test_2 `-- test_c.py
unittest
和 nose
均支持測試前置和清理的生效級別:測試方法、測試類和測試模塊。
pytest
的測試夾具一樣支持各種生效級別,且更加豐富。經過在pytest.fixture 中指定 scope
參數來設置:
當咱們指定生效級別爲模塊級時,示例以下:
import pytest import smtplib @pytest.fixture(scope="module") def smtp_connection(): return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
pytest
的測試夾具也可以實現測試前置和清理,經過 yield
語句來拆分這兩個邏輯,寫法變得很簡單,如:
import smtplib import pytest @pytest.fixture(scope="module") def smtp_connection(): smtp_connection = smtplib.SMTP("smtp.gmail.com", 587, timeout=5) yield smtp_connection # provide the fixture value print("teardown smtp") smtp_connection.close()
在上述示例中,yield smtp_connection
及前面的語句至關於測試前置,經過 yield
返回準備好的測試資源 smtp_connection
; 然後面的語句則會在用例執行結束(確切的說是測試夾具的生效級別的聲明週期結束時)後執行,至關於測試清理。
若是生成測試資源(如示例中的 smtp_connection
)的過程支持 with
語句,那麼還能夠寫成更加簡單的形式:
@pytest.fixture(scope="module") def smtp_connection(): with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection: yield smtp_connection # provide the fixture value
pytest
的測試夾具除了文中介紹到的這些功能,還有諸如參數化夾具、工廠夾具、在夾具中使用夾具等更多高階玩法。
pytest
除了支持 unittest
和 nosetest
的跳過測試和預計失敗的方式外,還在 pytest.mark
中提供對應方法:
示例以下:
@pytest.mark.skip(reason="no way of currently testing this") def test_mark_skip(): ... def test_skip(): if not valid_config(): pytest.skip("unsupported configuration") @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") def test_mark_skip_if(): ... @pytest.mark.xfail def test_mark_xfail(): ...
pytest
除了支持 unittest
中的 TestCase.subTest
,還支持一種更爲靈活的子測試編寫方式,也就是 參數化測試
,經過 pytest.mark.parametrize
裝飾器實現。
在下面的示例中,定義一個 test_eval
測試函數,經過 pytest.mark.parametrize
裝飾器指定 3 組參數,則將生成 3 個子測試:
@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)]) def test_eval(test_input, expected): assert eval(test_input) == expected
示例中故意讓最後一組參數致使失敗,運行用例能夠看到豐富的測試結果輸出:
========================================= test session starts ========================================= platform darwin -- Python 3.7.1, pytest-4.0.1, py-1.7.0, pluggy-0.8.0 rootdir: /Users/prodesire/projects/tests, inifile: plugins: cov-2.6.0 collected 3 items test.py ..F [100%] ============================================== FAILURES =============================================== __________________________________________ test_eval[6*9-42] __________________________________________ test_input = '6*9', expected = 42 @pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)]) def test_eval(test_input, expected): > assert eval(test_input) == expected E AssertionError: assert 54 == 42 E + where 54 = eval('6*9') test.py:6: AssertionError ================================= 1 failed, 2 passed in 0.09 seconds ==================================
若將參數換成 pytest.param
,咱們還能夠有更高階的玩法,好比知道最後一組參數是失敗的,因此將它標記爲 xfail:
@pytest.mark.parametrize( "test_input,expected", [("3+5", 8), ("2+4", 6), pytest.param("6*9", 42, marks=pytest.mark.xfail)], ) def test_eval(test_input, expected): assert eval(test_input) == expected
若是測試函數的多個參數的值但願互相排列組合,咱們能夠這麼寫:
@pytest.mark.parametrize("x", [0, 1]) @pytest.mark.parametrize("y", [2, 3]) def test_foo(x, y): pass
上述示例中會分別把 x=0/y=2
、x=1/y=2
、x=0/y=3
和x=1/y=3
帶入測試函數,視做四個測試用例來執行。
pytest
的測試結果輸出相比於 unittest
和 nose
來講更爲豐富,其優點在於:
若是對軟件測試、接口測試、自動化測試、面試經驗交流。感興趣能夠加軟件測試交流:1085991341,還會有同行一塊兒技術交流。
pytest
的插件十分豐富,並且即插即用,做爲使用者不須要編寫額外代碼。
此外,得益於 pytest
良好的架構設計和鉤子機制,其插件編寫也變得容易上手。
三篇關於 Python 測試框架的介紹到這裏就要收尾了。寫了這麼多,各位看官怕也是看得累了。咱們不妨羅列一個橫向對比表,來總結下這些單元測試框架的異同:
unittest | nose | nose2 | pytest | |
---|---|---|---|---|
自動發現用例 |
|
|
|
|
指定(各級別)用例執行 |
|
|
|
|
支持 assert 斷言 | 弱 | 弱 | 弱 | 強 |
測試夾具 |
|
|
|
|
測試夾具種類 | 前置和清理 | 前置和清理 | 前置和清理 | 前置、清理、內置各種 fixtures,自定義各種 fixtures |
測試夾具生效級別 | 方法、類、模塊 | 方法、類、模塊 | 方法、類、模塊 | 方法、類、模塊、包、會話 |
支持跳過測試和預計失敗 |
|
|
|
|
子測試 |
|
|
|
|
測試結果輸出 | 通常 | 較好 | 較好 | 好 |
插件 | - | 較豐富 | 通常 | 豐富 |
鉤子 | - | - |
|
|
社區生態 | 做爲標準庫,由官方維護 | 中止維護 | 維護中,活躍度低 | 維護中,活躍度高 |
Python 的單元測試框架看似種類繁多,實則是一代代的進化,有跡可循。抓住其特色,結合使用場景,就能容易的作出選擇。
若你不想安裝或不容許第三方庫,那麼 unittest
是最好也是惟一的選擇。反之,pytest
無疑是最佳選擇,衆多 Python 開源項目都是使用 pytest
做爲單元測試框架。以上內容就是本篇的所有內容以上內容但願對你有幫助,有被幫助到的朋友歡迎點贊,評論。