Try   HackMD

pytest fixture的分級(scope)

tags: unit-test pytest

閱讀本文前,我們會假設各位都知道 fixture是一個"測試單元都能拿去用的公共物件"

先上個例子

@pytest.fixture def db(): # open db yield # close db def test1(db): logging.info("test1 use db") def test2(db): logging.info("test2 use db")

現在我們有一個fixture跟兩個測試,如果他們都需要跟db交互的話,會發生什麼事呢?

在上面這個範例中,fixture會被運行兩次。但如果在測試情境上,我們覺得db其實不用每次都重新打開一次要怎麼辦?

當我們在cmd中運行pytest的瞬間,這個fixture就會在每個測試開始的時候將db打開並yield住,保持db開啟,並在測試結束時關閉db,沒有問題,也不會出錯。

但今天如果這個打開db的行為是另外一個相當耗時或記憶體的行為時,又要怎麼辦呢?

於是在pytest fixture中,有scope這個參數,可以讓撰寫測試的各位決定fixture的觸發頻率,scope一共有五種。

Function

預設的scope層級,會在每個test function結束後進行teardown並結束

class

如果在撰寫測試單元時有用class把測試包起來,那麼在class內的測試可以用這個scope來確認在同個class內fixture只觸發一次

import pytest @pytest.fixture(scope="class") def my_fixture(): print("Setup") yield print("Teardown") class TestClass: def test_foo(self, my_fixture): print("my_fixture setup once in this class") def test_bar(self, my_fixture): print("my_fixture setup once in this class")

module

在同個file內的測試都會確保只觸發一次fixture
假設我們的資料夾結構長這樣

project/
├── tests/
│   ├── conftest.py
│   └── test_module1.py
│   └── test_module2.py

而我們的fixture長這樣

@pytest.fixture(scope="module") def db(): # open db yield # close db

那麼test_module1.py與test_module2.py會各自觸發一次fixture

package

在同個folder內的測試會觸發一次fixture
用跟上面一樣的資料夾結構

project/
├── tests/
│   ├── conftest.py
│   └── test_module1.py
│   └── test_module2.py

@pytest.fixture(scope="package") def db(): # open db yield # close db

現在test_module1.py與test_module2.py只會觸發一次fixture了

session

在整個測試流程中此fixture只會觸發一次

總結

其實還有很多小坑,包含fixture運作的順序,scope可以是動態等等,但本篇範例主要是讓各位理解,其實pytest可以用scope來讓撰寫測試的各位決定fixture該在什麼時候觸發。

有了以上這些分級的概念,相信各位在撰寫自己的測試時,就能根據不同fixture function的用途分配scope,最終達到節省測試總時長與消耗資源的目的。

如果有更多問題也歡迎詢問,或查閱官方文件如下:

https://docs.pytest.org/en/stable/how-to/fixtures.html#how-to-fixtures