Pytest参数选项自由执行测试用例详解(二)
??
? ? ? 運行pytest可以指定目錄和文件,如果不指定,pytest會搜索當前目錄及其子目錄中以test_開頭或以_test結尾得測試函數。我們把pytest搜索測試文件和測試用例的過程稱為測試搜索(test discovery)。只要遵循pytest的命名規則,pytest就能自動搜索所有待執行的測試用例。所有的包必須要有init.py文件(在使用各種編輯器時會自動生成)
1、測試文件命名規則,test_xxx.py或xxx_test.py
2、方法、測試函數命名規則,test_xxx
3、測試類命名規則,Testxxx,并且不能帶有 init 方法
Pytest參數選項在腳本中和命令行用法詳解(一)
-k選項??-K?EXPRESSION
使用表達式指定某個關鍵字的測試用例,如果某測試名是唯一的或多個測試名的前綴或后綴相同,可快速匹配,匹配范圍是全局相同目錄或下層目錄所有(包名、文件名、類名、函數名為變量),文件名、類名、函數名,必須是test_開頭或_test結尾的。
pytest.main(['-k','關鍵字']) 關鍵字包含于或等于文件名、類名、函數名,多個關鍵字之間用and、or、not連接測試代碼如下:
執行匹配包名,運行了3條用例
pytest.main(['-v','-k','chengzi'])
if __name__ == '__main__':pytest.main(['-v','-k','chengzi'])"C:\Program Files\Python35\python.exe" C:/Users/wangli/PycharmProjects/untitled/chengzi/test_04.py ============================= test session starts ============================= platform win32 -- Python 3.5.2, pytest-6.1.1, py-1.9.0, pluggy-0.13.1 -- C:\Program Files\Python35\python.exe cachedir: .pytest_cache metadata: {'Packages': {'pluggy': '0.13.1', 'py': '1.9.0', 'pytest': '6.1.1'}, 'Platform': 'Windows-10-10.0.18362-SP0', 'JAVA_HOME': 'C:\\Program Files\\Java\\jdk1.8.0_101', 'Python': '3.5.2', 'Plugins': {'allure-pytest': '2.8.18', 'metadata': '1.8.0', 'rerunfailures': '9.1.1', 'html': '1.22.0'}} rootdir: C:\Users\wangli\PycharmProjects\untitled\chengzi plugins: allure-pytest-2.8.18, html-1.22.0, metadata-1.8.0, rerunfailures-9.1.1 collecting ... collected 3 itemstest_04.py::TestClass::test_one FAILED [ 33%] test_04.py::TestClass::test_two PASSED [ 66%] test_04.py::TestClass::test_two2 PASSED [100%]================================== FAILURES =================================== _____________________________ TestClass.test_one ______________________________self = <chengzi.test_04.TestClass object at 0x000001F27E537B70>def test_one(self): > assert 2 == 1 E assert 2 == 1 E +2 E -1test_04.py:5: AssertionError =========================== short test summary info =========================== FAILED test_04.py::TestClass::test_one - assert 2 == 1 ========================= 1 failed, 2 passed in 0.63s =========================Process finished with exit code 0執行匹配文件名,運行了3條用例
pytest.main(['-v','-k','test_04.py'])if __name__ == '__main__':pytest.main(['-v','-k','test_04.py'])"C:\Program Files\Python35\python.exe" C:/Users/wangli/PycharmProjects/untitled/chengzi/test_04.py ============================= test session starts ============================= platform win32 -- Python 3.5.2, pytest-6.1.1, py-1.9.0, pluggy-0.13.1 -- C:\Program Files\Python35\python.exe cachedir: .pytest_cache metadata: {'Platform': 'Windows-10-10.0.18362-SP0', 'Python': '3.5.2', 'Packages': {'py': '1.9.0', 'pluggy': '0.13.1', 'pytest': '6.1.1'}, 'JAVA_HOME': 'C:\\Program Files\\Java\\jdk1.8.0_101', 'Plugins': {'rerunfailures': '9.1.1', 'html': '1.22.0', 'allure-pytest': '2.8.18', 'metadata': '1.8.0'}} rootdir: C:\Users\wangli\PycharmProjects\untitled\chengzi plugins: allure-pytest-2.8.18, html-1.22.0, metadata-1.8.0, rerunfailures-9.1.1 collecting ... collected 3 itemstest_04.py::TestClass::test_one FAILED [ 33%] test_04.py::TestClass::test_two PASSED [ 66%] test_04.py::TestClass::test_two2 PASSED [100%]================================== FAILURES =================================== _____________________________ TestClass.test_one ______________________________self = <chengzi.test_04.TestClass object at 0x000002031D345588>def test_one(self): > assert 2 == 1 E assert 2 == 1 E +2 E -1test_04.py:6: AssertionError =========================== short test summary info =========================== FAILED test_04.py::TestClass::test_one - assert 2 == 1 ========================= 1 failed, 2 passed in 0.48s =========================Process finished with exit code 0執行匹配類名,運行了3條用例
pytest.main(['-v','-k','Class'])if __name__ == '__main__':pytest.main(['-v','-k','Class'])"C:\Program Files\Python35\python.exe" C:/Users/wangli/PycharmProjects/untitled/chengzi/test_04.py ============================= test session starts ============================= platform win32 -- Python 3.5.2, pytest-6.1.1, py-1.9.0, pluggy-0.13.1 -- C:\Program Files\Python35\python.exe cachedir: .pytest_cache metadata: {'Plugins': {'html': '1.22.0', 'rerunfailures': '9.1.1', 'metadata': '1.8.0', 'allure-pytest': '2.8.18'}, 'Packages': {'pluggy': '0.13.1', 'py': '1.9.0', 'pytest': '6.1.1'}, 'JAVA_HOME': 'C:\\Program Files\\Java\\jdk1.8.0_101', 'Python': '3.5.2', 'Platform': 'Windows-10-10.0.18362-SP0'} rootdir: C:\Users\wangli\PycharmProjects\untitled\chengzi plugins: allure-pytest-2.8.18, html-1.22.0, metadata-1.8.0, rerunfailures-9.1.1 collecting ... collected 3 itemstest_04.py::TestClass::test_one FAILED [ 33%] test_04.py::TestClass::test_two PASSED [ 66%] test_04.py::TestClass::test_two2 PASSED [100%]================================== FAILURES =================================== _____________________________ TestClass.test_one ______________________________self = <chengzi.test_04.TestClass object at 0x0000023ED5C68B00>def test_one(self): > assert 2 == 1 E assert 2 == 1 E +2 E -1test_04.py:6: AssertionError =========================== short test summary info =========================== FAILED test_04.py::TestClass::test_one - assert 2 == 1 ========================= 1 failed, 2 passed in 0.21s =========================Process?finished?with?exit?code?0執行匹配函數名,函數名中含test且含two的,運行了2條用例
-m選項? -m MARKEXPR
標記(marker)用于標記測試并分組,以便快速選中并運行,匹配運行所有標記了裝飾器@pytest.mark.marker的類或方法的測試用例,標記名可自行定義,如果標記名為run_all,可使用@pytest.mark.run_all裝飾類或函數來做標記,匹配范圍是全局相同目錄或下層目錄所有(類裝飾器的標記名、函數裝飾器的標記名),文件名、類名、函數名,必須是test_開頭或_test結尾的。
pytest.main(['-v','-m','關鍵字']) 關鍵字等于類裝飾器或函裝飾器@pytest.mark.標記名,里的標記名,多個關鍵字之間用and、or、not連接測試代碼目錄:
測試代碼:
chengzi\test_01.py import pytestclass Test_Class(object):def test_one(self):assert 2 == 1@pytest.mark.run_twodef testtwo(self):assert 2 == 2def test_two2(self):assert 2 == 2if __name__ == '__main__':pytest.main(['-v','-m','run_one or run_all'])juzi\test_02.py @pytest.mark.run_all class TestClass(object):def test_one(self):assert 2 == 1def test_two(self):assert 2 == 2def test_two2(self):assert 2 == 2if __name__ == '__main__':pytest.main(['-v','-m','run_one or run_all'])import pytesttest_03.py class TestClass(object):@pytest.mark.run_onedef test_one(self):assert 2 == 1def test_two(self):assert 2 == 2def test_two2(self):assert 2 == 2if __name__ == '__main__':pytest.main(['-v','-m','run_one or run_all'])test_04.py???? class TestClass(object):def test_onqq(self):assert 2 == 1def test_two(self):assert 2 == 2@pytest.mark.two2def test_two2(self):assert 2 == 2if __name__ == '__main__':pytest.main(['-v','-m','run_one?or?run_all'])??執行匹配類或函數裝飾器里,標記名為run_one或run_all的測試用例,可以看到匹配運行了4條用例
pytest.main(['-v','-m','run_one or run_all'])if __name__ == '__main__':pytest.main(['-v','-m','run_one or run_all'])"C:\Program Files\Python35\python.exe" C:/Users/wangli/PycharmProjects/untitled/test_04.py ============================= test session starts ============================= platform win32 -- Python 3.5.2, pytest-6.1.1, py-1.9.0, pluggy-0.13.1 -- C:\Program Files\Python35\python.exe cachedir: .pytest_cache metadata: {'JAVA_HOME': 'C:\\Program Files\\Java\\jdk1.8.0_101', 'Platform': 'Windows-10-10.0.18362-SP0', 'Python': '3.5.2', 'Plugins': {'allure-pytest': '2.8.18', 'html': '1.22.0', 'metadata': '1.8.0', 'rerunfailures': '9.1.1'}, 'Packages': {'pytest': '6.1.1', 'pluggy': '0.13.1', 'py': '1.9.0'}} rootdir: C:\Users\wangli\PycharmProjects\untitled plugins: allure-pytest-2.8.18, html-1.22.0, metadata-1.8.0, rerunfailures-9.1.1 collecting ... collected 12 items / 8 deselected / 4 selectedtest_03.py::TestClass::test_one FAILED [ 25%] juzi/test_02.py::TestClass::test_one FAILED [ 50%] juzi/test_02.py::TestClass::test_two PASSED [ 75%] juzi/test_02.py::TestClass::test_two2 PASSED [100%]================================== FAILURES =================================== _____________________________ TestClass.test_one ______________________________self = <test_03.TestClass object at 0x00000232EDB1BF60>@pytest.mark.run_onedef test_one(self): > assert 2 == 1 E assert 2 == 1 E +2 E -1test_03.py:7: AssertionError _____________________________ TestClass.test_one ______________________________self = <juzi.test_02.TestClass object at 0x00000232EDB0B080>def test_one(self): > assert 2 == 1 E assert 2 == 1 E +2 E -1juzi\test_02.py:6: AssertionError ============================== warnings summary =============================== test_03.py:5C:\Users\wangli\PycharmProjects\untitled\test_03.py:5: PytestUnknownMarkWarning: Unknown pytest.mark.run_one - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html@pytest.mark.run_onetest_04.py:12C:\Users\wangli\PycharmProjects\untitled\test_04.py:12: PytestUnknownMarkWarning: Unknown pytest.mark.two2 - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html@pytest.mark.two2chengzi\test_01.py:8C:\Users\wangli\PycharmProjects\untitled\chengzi\test_01.py:8: PytestUnknownMarkWarning: Unknown pytest.mark.run_two - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html@pytest.mark.run_twojuzi\test_02.py:3C:\Users\wangli\PycharmProjects\untitled\juzi\test_02.py:3: PytestUnknownMarkWarning: Unknown pytest.mark.run_all - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html@pytest.mark.run_all-- Docs: https://docs.pytest.org/en/stable/warnings.html =========================== short test summary info =========================== FAILED test_03.py::TestClass::test_one - assert 2 == 1 FAILED juzi/test_02.py::TestClass::test_one - assert 2 == 1 ============ 2 failed, 2 passed, 8 deselected, 4 warnings in 0.24s ============Process finished with exit code 0-V(--verbose)選項
輸出更加詳細的信息,比如用例所在的包、py文件、類名及用例名稱等測試的名字和結果都會顯示出來,最明顯區別就是每個文件中的測試用例都會占一行(先前是每個文件占一行)
-s選項
允許終端在測試運行時,輸出用例中的調式信息,包括任何符合標準的輸出流信息,比如print的打印信息等。
pytest.main(['-s'])測試代碼:
test-04.py import pytestclass TestClass(object):def test_one(self):print('this is test_one')assert 2 == 1def test_two(self):print('this is test_two')assert 2 == 2@pytest.mark.two2def test_two2(self):print('this is test_two2')assert 2 == 2if __name__ == '__main__':pytest.main(['-s'])"C:\Program Files\Python35\python.exe" C:/Users/wangli/PycharmProjects/untitled/test_04.py ============================= test session starts ============================= platform win32 -- Python 3.5.2, pytest-6.1.1, py-1.9.0, pluggy-0.13.1 rootdir: C:\Users\wangli\PycharmProjects\untitled plugins: allure-pytest-2.8.18, html-1.22.0, metadata-1.8.0, rerunfailures-9.1.1 111 collected 12 itemstest_03.py F.. test_04.py this is test_one Fthis is test_two .this is test_two2 . chengzi\test_01.py F.. juzi\test_02.py?F..-x選項
debug調試時,我們希望遇到斷言失敗時,立即全局停止,這時就可以用到-x
測試代碼如下:
#設置用例1和用例3斷言成功,斷言2失敗 #執行后,用例1執行了,用例2執行斷言失敗,腳本停止,用例3沒有繼續執行 import?pytestclass TestClass(object):def test_one(self):print('this is test_one')assert 2 == 2def test_two(self):print('this is test_two')assert 2 == 1def test_two2(self):print('this is test_two2')assert 2 == 2if __name__ == '__main__':pytest.main(['-x','test_04.py'])"C:\Program Files\Python35\python.exe" C:/Users/wangli/PycharmProjects/untitled/test_04.py ============================= test session starts ============================= platform win32 -- Python 3.5.2, pytest-6.1.1, py-1.9.0, pluggy-0.13.1 rootdir: C:\Users\wangli\PycharmProjects\untitled plugins: allure-pytest-2.8.18, html-1.22.0, metadata-1.8.0, rerunfailures-9.1.1 collected 3 itemstest_04.py .F================================== FAILURES =================================== _____________________________ TestClass.test_two ______________________________self = <test_04.TestClass object at 0x000001465DA2D780>def test_two(self):print('this is test_two') > assert 2 == 1 E assert 2 == 1test_04.py:11: AssertionError ---------------------------- Captured stdout call ----------------------------- this is test_two =========================== short test summary info =========================== FAILED test_04.py::TestClass::test_two - assert 2 == 1 !!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!! ========================= 1 failed, 1 passed in 0.88s =========================Process?finished?with?exit?code?0--maxfail=num
與-x選項類似,測試用例達到指定失敗次數,就會全局停止,不在執行,假設允許pytest失敗2次后停止,則可以使用--maxfail=2,明確指定可失敗次數。
pytest.main(['--maxfail=2','test_04.py'])測試代碼如下:
--lf(--last-failed)選項
執行上次斷言失敗的用例A,當用例A一直是斷言失敗,運行腳本一直只執行用例A;當用例A斷言成功后,沒有失敗的用例了,會執行全部用例,如果又有斷言失敗的用例,下次會執行運行失敗的用例(運行腳本會執行上次斷言失敗的用例,沒有失敗用例會執行所有用例)
pytest.main(['--lf','test_04.py'])#設置test_two用例斷言失敗,運行j腳本后只跑了test_two用例 import?pytest class TestClass(object):def test_one(self):print('this is test_one')assert 2 == 2def test_two(self):print('this is test_two')assert 2 == 1def test_two2(self):print('this is test_two2')assert 2 == 2if __name__ == '__main__':pytest.main(['--lf','test_04.py'])collected 3 items / 2 deselected / 1 selected run-last-failure: rerun previous 1 failuretest_04.py F [100%]================================== FAILURES =================================== _____________________________ TestClass.test_two ______________________________self = <test_04.TestClass object at 0x0000028C5FB3A198>def test_two(self):print('this is test_two') > assert 2 == 1 E assert 2 == 1test_04.py:11: AssertionError ---------------------------- Captured stdout call ----------------------------- this is test_two =========================== short test summary info =========================== FAILED test_04.py::TestClass::test_two - assert 2 == 1 ======================= 1 failed, 2 deselected in 0.15s =======================Process finished with exit code 0#把用例test_two改成斷言正確的,再次運行腳本,仍執行了test_two用例,發現test_two # 用例斷言成功了 import?pytest class TestClass(object):def test_one(self):print('this is test_one')assert 2 == 2def test_two(self):print('this is test_two')assert 2 == 2def test_two2(self):print('this is test_two2')assert 2 == 2if __name__ == '__main__':pytest.main(['--lf','test_04.py'])collected?3?items?/?2?deselected?/?1?selected run-last-failure: rerun previous 1 failuretest_04.py . [100%]======================= 1 passed, 2 deselected in 0.09s =======================Process finished with exit code 0#再次運行腳本,執行了全部用例 import?pytest class TestClass(object):def test_one(self):print('this is test_one')assert 2 == 2def test_two(self):print('this is test_two')assert 2 == 2def test_two2(self):print('this is test_two2')assert 2 == 2if __name__ == '__main__':pytest.main(['--lf','test_04.py'])collected 3 items run-last-failure: 13 known failures not in selected teststest_04.py ... [100%]============================== 3 passed in 0.05s ==============================Process finished with exit code 0--ff(--failed-first)選項
--ff選項,運行腳本,會優先執行上次斷言失敗的用例,再依次執行其他用例,直至運行腳本,上次斷言失敗的用例這次變為斷言成功,下次再運行腳本,才會按正常用例順序依次執行,與--lf選項作用基本相同,不同之處--ff會運行剩下 的測試用例。
pytest.main(['-s','--ff','test_04.py'])#?設置test_two2斷言失敗,首次運行腳本,未優先執行test_two2, # 結果test_two2斷言失敗 import?pytest class TestClass(object):def test_one(self):print('this is test_one')assert 2 == 2def test_two(self):print('this is test_two')assert 2 == 2def test_two2(self):print('this is test_two2')assert 2 == 1if __name__ == '__main__':pytest.main(['-s','--ff','test_04.py'])collected 3 items run-last-failure: 13 known failures not in selected teststest_04.py this is test_one .this is test_two .this is test_two2 F================================== FAILURES =================================== _____________________________ TestClass.test_two2 _____________________________self = <test_04.TestClass object at 0x000001A124621CF8>def test_two2(self):print('this is test_two2') > assert 2 == 1 E assert 2 == 1test_04.py:15: AssertionError =========================== short test summary info =========================== FAILED test_04.py::TestClass::test_two2 - assert 2 == 1 ========================= 1 failed, 2 passed in 0.17s =========================Process finished with exit code 0#?腳本不變,再次運行一次,發現優先執行了上次斷言失敗的用例test_two2 #?再按順序依次執行,結果test_two2斷言失敗 collected 3 items run-last-failure: rerun previous 1 failure firsttest_04.py this is test_two2 Fthis is test_one .this is test_two .================================== FAILURES =================================== _____________________________ TestClass.test_two2 _____________________________self = <test_04.TestClass object at 0x000002C5D0F03668>def test_two2(self):print('this is test_two2') > assert 2 == 1 E assert 2 == 1test_04.py:15: AssertionError =========================== short test summary info =========================== FAILED test_04.py::TestClass::test_two2 - assert 2 == 1 ========================= 1 failed, 2 passed in 0.10s =========================Process finished with exit code 0#?test_two2用例改成斷言成功,再次執行腳本,發現還是優先執行了上次斷言失敗的用例test_two2 #?用例,再按順序依次執行,結果test_two2斷言成功def test_two2(self):print('this is test_two2')assert 2 == 2collected 3 items run-last-failure: rerun previous 1 failure firsttest_04.py this is test_two2 .this is test_one .this is test_two .============================== 3 passed in 0.07s ==============================Process finished with exit code 0#?腳本不變,再運行依次腳本,發現未優先執行test_two2用例,因為上次結果 #?沒有斷言失敗的,所有不會優先執行,正常依次執行collected 3 items run-last-failure: 13 known failures not in selected teststest_04.py this is test_one .this is test_two .this is test_two2 .============================== 3 passed in 0.03s ==============================Process finished with exit code 0--tb=style選項
捕捉到失敗時輸出信息的顯示方式,某個測試用例失敗后,pytest會列舉出失敗信息,包括失敗在哪一行、是什么失敗、怎么失敗的,此過程稱“信息回溯”它對找到問題很有幫助,但有時也會對多余的信息感到厭煩,這時--tb=style選項就有用武之地了,style類型有short、line、no。short模式僅輸出assert的一行以及系統判定內容(不顯示上下文);line模式只使用一行輸出顯示所有的錯誤信息;no模式則直接屏蔽全部回溯信息
總結
以上是生活随笔為你收集整理的Pytest参数选项自由执行测试用例详解(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Python】PyCryptodome
- 下一篇: php 静态页面模板类,dedetag.