pytestにおけるfixtureとは
- テストの事前処理、事後処理を記載できるpytestの機能です。
- SetUp(事前処理), TearDown(事後処理)を1つの関数で書けます。
-
setup_class
,setup_function
のように、スコープごとに違う関数を呼ぶ必要がありません。 - fixtureからfixtureを呼び出すことができ、処理を階層的に記述できます。
SetUp, TearDownじゃダメなの?
- ダメかと言われるとダメじゃありません。チーム内でスタイルが統一されている方が大事です。
- 機能面だけで言うと、pytestにおいてはfixtureを利用する方がリッチではあります。
- ブラウザ操作自動化ツールであるPlaywright for Pythonはfixtureを利用しています。
- 上記を採用する場合、可読性と保守性の観点からfixtureに統一することを勧めます。
基本的な構文
import pytest
@pytest.fixture(scope="function") # fixtureの宣言
def hoge():
# setup
message = "ほげ"
# test
yield message
# teardown
print("done")
def test_print(hoge):
print(hoge)
実行結果:
ほげ
done
上記のように、
-
@pytest.fixture(scope="~~~")
とデコレート文を書いた次の関数がfixtureになります。 -
scope="~~~"
は実行の粒度を指します。(参考)- "function" : テストメソッドごと
- "class" : テストクラスごと
- "module" : スクリプト(ファイル)ごと
- "session" : pytestコマンド実行ごと
- 呼ぶ側は引数にfixtureを指定します。
- fixture側で
yield
(続く処理がない場合はreturn
)すると呼び出し元に値を引き渡せます。
とりあえずこれだけ知っておけば最低限使えると思います。
実例: fixtureを使ったディレクトリ作成① autouse=True
ではもう少し深堀りして、fixtureを使ったディレクトリ作成を試します。
これはテストのスクリーンショットを実行ごとに保存する際などに使えます。
ここでは下記のディレクトリ構成を想定し、テスト実行時にその実行日時のディレクトリをoutputs/
以下に生成します。
.
tests/
outputs/
{%y%m%d-%H%M%S}/ # 今回の作成対象ディレクトリ
conftest.py
test_makedir.py
fixtureは設定ファイルconftest.py
中に記載すると全てのテストスクリプトから呼び出せます。
import os
from datetime import datetime
import pytest
PATH_OUTPUTS = "./tests/outputs"
@pytest.fixture(scope="session", autouse=True)
def parentdir():
name_parentdir = datetime.now().strftime("%y%m%d-%H%M%S")
path_parentdir = os.path.join(PATH_OUTPUTS, name_parentdir)
os.makedirs(path_parentdir)
return path_parentdir
class TestMakeDir:
def test_makedir(self): # 以下省略
テストメソッドの引数にparentdir
を指定してないことに気付いたら鋭いですね。
autouse=True
を付けることで、テストメソッドで呼び出さなくてもfixtureを呼び出せます。
実例: fixtureを使ったディレクトリ作成② 階層的なfixtureの呼び出しとbuilt-in fixture
もう少し複雑な例として、上記の実例を修正を加え、テストメソッドの属するクラスごとに更にディレクトリを作成してみます。
下記のディレクトリ構成を想定し、テスト実行時にoutputs/{%y%m%d-%H%M%S}/
以下に、
実行されたテストクラスのディレクトリを生成します。
.
tests/
outputs/
{%y%m%d-%H%M%S}/
TestMakeDir # 今回の作成対象ディレクトリ
TestMakeDirTwo # 今回の作成対象ディレクトリ
conftest.py
test_makedir.py
この場合、conftest.py
は以下のようになります。
import os
from datetime import datetime
import pytest
PATH_OUTPUTS = "./tests/outputs"
@pytest.fixture(scope="session", autouse=True)
def parentdir():
name_parentdir = datetime.now().strftime("%y%m%d-%H%M%S")
path_parentdir = os.path.join(PATH_OUTPUTS, name_parentdir)
os.makedirs(path_parentdir)
return path_parentdir
@pytest.fixture(scope="class")
def classdir(request, parentdir) -> str:
path_classdir = os.path.join(parentdir, request.node.name)
os.makedirs(path_classdir)
return path_classdir
class TestMakeDir:
def test_makedir(self, classdir): # 以下省略
class TestMakeDirTwo:
def test_makedir_two(self, classdir): # 以下省略
まずfixtureclassdir
の引数にparentdir
が指定されていることに注目してください。
ここではfixtureparentdir
が返却するpath_parentdir
を変数parentdir
に格納しています。
もう一つ、fictureclassdir
にはrequest
というimportもされていない謎の引数もありますね。
これはpytestにビルトインされているfixtureで、importなしで使えます。
request.node.name
には呼び出したノードの名前、すなわちTestMakeDir
やTestMakeDirTwo
が格納されています。これで最終的な出力ディレクトリが生成できます。
request
の他にも色々なbuilt-in fixtureがあるので、調べてみてください。
まとめ
fixtureを使いこなすと夢が広がります。
参考
しっかり理解したい方は公式ドキュメントを参照ください。