Skip to content

Latest commit

 

History

History

pytest_tutorial

pytest 教學

官網文件可參考 pytest

先安裝 pytest

pip3 install pytest

demo1_test.py 基本的示範 pytest 的寫法

執行指令如下

pytest demo1_test.py -v -s

記得要加入 -s (才能顯示 print 資訊)

執行結果

collected 1 item

demo1_test.py::test_answer PASSED

pytest 也有 初始化 setup 和 清除 teardown 的方法,

它有分很多級別,

setup_module/teardown_module

setup_function/teardown_function

setup_class/teardown_class

setup_method/teardown_methond euqal setup/teardown

demo2_module_test.py 範例為使用 *_module 級別,

setup 和 teardown 只會在最開始和最後個別執行一次

demo2_module_test.py::TestDemo2::test_case1 setup...
run test_case1
PASSED
demo2_module_test.py::TestDemo2::test_case2 run test_case2
PASSED
demo2_module_test.py::TestDemo2::test_case3 run test_case3
PASSEDteardown...

demo2_function_test.py 範例為使用 *_function 級別,

setup 和 teardown 在每個 function 中都會生效

demo2_function_test.py::test_case1 setup...
run test_case1
PASSEDteardown...

demo2_function_test.py::test_case2 setup...
run test_case2
PASSEDteardown...

demo2_function_test.py::test_case3 setup...
run test_case3
PASSEDteardown...

demo2_class_test.py 範例為使用 *_class 級別,

setup 和 teardown 只會在 class 最開始和最後個別執行一次

demo2_class_test.py::TestDemo2::test_case1 setup...
run test_case1
PASSED
demo2_class_test.py::TestDemo2::test_case2 run test_case2
PASSED
demo2_class_test.py::TestDemo2::test_case3 run test_case3
PASSEDteardown...

demo2_method_test.py 範例為使用 *_method 級別,

setup 和 teardown 在每個 function 中都會生效

demo2_method_test.py::TestDemo2::test_case1 setup...
run test_case1
PASSEDteardown...

demo2_method_test.py::TestDemo2::test_case2 setup...
run test_case2
PASSEDteardown...

demo2_method_test.py::TestDemo2::test_case3 setup...
run test_case3
PASSEDteardown...

但是在 pytest 中, 有更好的方法可以取代掉這種比較舊的寫法 😄 (後面會介紹到)

demo3_test.py fixture 這個方法主要是用來改善 setup/teardown 這樣的架構,

test_case2 會去取 hello 的值, pytest 會去找擁有 @pytest.fixture() hello 的方法,

demo3_test.py::Test_Demo3::test_case1 run test_case1
PASSED
demo3_test.py::Test_Demo3::test_case2 hello world
run test_case2
66
PASSED
demo3_test.py::Test_Demo3::test_case3 run test_case3
PASSED

demo3_share_test.py 這個範例和上面是一樣的, 只是將 hello 獨立出來,

寫在 conftest.py 中, pytest 會自己到目錄底下尋找.

demo4_test.py 可以使用 yield 來完成 setup 和 teardown 的功能.

demo4_test.py::Test_Demo4::test_case1 run test_case1
PASSED
demo4_test.py::Test_Demo4::test_case2 start hello world
run test_case2
66
PASSEDend hello world

demo4_test.py::Test_Demo4::test_case3 run test_case3
PASSED

demo5_test.py 除了用 yield 來完成 setup 和 teardown 的功能之外,

也可以用 addfinalizer 這個方法.

demo5_test.py::Test_Demo5::test_case1 run test_case1
PASSED
demo5_test.py::Test_Demo5::test_case2 start hello world
run test_case2
66
PASSEDend hello world

demo5_test.py::Test_Demo5::test_case3 run test_case3
PASSED

fixture 也有所謂的 Scope (預設為 function), 分別為

function 每個 function 都會執行一次

class 每個 class 只執行一次

module 每個 module 只執行一次

session 整個 session 只執行一次

demo6_test.py 該範例為 function,

每一個 function 都會調用一次,

demo6_test.py::Test_Demo6::test_case1 hello world
run test_case1
66
PASSED
demo6_test.py::Test_Demo6::test_case2 hello world
run test_case2
66
PASSED
demo6_test.py::Test_Demo6::test_case3 hello world
run test_case3
66
PASSED

demo7_test.py 該範例為 class,

只調用了一次,

demo7_test.py::Test_Demo7::test_case1 hello world
run test_case1
PASSED
demo7_test.py::Test_Demo7::test_case2 run test_case2
PASSED
demo7_test.py::Test_Demo7::test_case3 run test_case3
PASSED

demo8_test.py 該範例為 fixture 自動調用, 透過 autouse=True 參數,

因為 default 的 scope=function, 所以每個 function 都會被調用.

demo8_test.py::Test_Demo8::test_case1 hello world
run test_case1
PASSED
demo8_test.py::Test_Demo8::test_case2 hello world
run test_case2
PASSED
demo8_test.py::Test_Demo8::test_case3 hello world
run test_case3
PASSED

demo9_test.py 也可以透過 @pytest.mark.usefixtures 自動調用 fixture.

demo9_test.py::Test_Demo9::test_case1 hello world
run test_case1
PASSED
demo9_test.py::Test_Demo9::test_case2 hello world
run test_case2
PASSED
demo9_test.py::Test_Demo9::test_case3 hello world
run test_case3
PASSED

demo10_test.py 如果測試都是呼叫相同的參數, 可以將 fixture 參數化.

demo10_test.py::Test_Demo10::test_case1 run test_case1
PASSED
demo10_test.py::Test_Demo10::test_case2 run test_case2
PASSED
demo10_test.py::Test_Demo10::test_case3 run test_case3
PASSED
demo10_test.py::Test_Demo10::test_case4[data0] run test_case4
PASSED
demo10_test.py::Test_Demo10::test_case4[data1] run test_case4
PASSED
demo10_test.py::Test_Demo10::test_case4[data2] run test_case4
PASSED

demo11_test.py 或是透過 pytest.mark.parametrize 也可以參數化.

demo11_test.py::Test_Demo11::test_case1[input0] run test_case1
(1, 1, 2)
PASSED
demo11_test.py::Test_Demo11::test_case1[input1] run test_case1
(2, 2, 4)
PASSED
demo11_test.py::Test_Demo11::test_case1[input2] run test_case1
(3, 3, 6)
PASSED

demo12_test.py 預期的錯誤 (expected exceptions) pytest.raises

demo12_test.py::test_zero_division PASSED

demo13_test.py pass 預期的錯誤 pytest.mark.skip pytest.mark.xfail

demo13_test.py::test_f XFAIL
demo13_test.py::test_s SKIPPED (pass.....)

demo14_test.py custom markers (客製化 markers)

先建立 pytest.ini 設定你的 custom markers,

如果沒設定就使用裝飾器 @pytest.mark.name_of_the_mark, 會跳出錯誤

'slow' not found in `markers` configuration option

如果我想 pass custom markers, 請執行以下的指令.

pytest demo14_test.py -v -s --strict-markers -m "not slow"
demo14_test.py::test_a PASSED
pytest demo14_test.py -v -s
demo14_test.py::test_super_slow_test PASSED
demo14_test.py::test_a PASSED

最後, 如果你想要產生 coverage, 可參考 pytest-cov

安裝套件

pip install pytest-cov
pip install pytest-xdist
pytest --cov=.

img

pytest --cov=. --cov-report=term-missing --cov-report=html

執行成功會產生一個 htmlcov 資料夾, 打開底下的 index.html

img