Skip to content

Files

Latest commit

Apr 12, 2025
1667ca5 · Apr 12, 2025

History

History

pytest_tutorial

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
Apr 12, 2025
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021
Dec 8, 2021

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