pytestのfixtureを使えば、短いコードで簡単に、前処理・後処理を書けます。
この記事では、pytestのfixtureの書き方を詳しく解説します。
ちなみに、私の環境のバージョン情報は以下の通りです。
項目 | バージョン |
---|---|
Python | 3.10.6 |
pytest | 7.1.3 |
それでは、最後までよろしくお願いします。
fixtureとは?
fixture(フィクスチャ)を使えば、テストの前処理、後処理を書けます。
pytest以外のxUnitに詳しいなら、fixtureはsetup()、teardown()に相当すると考えると理解しやすいです。
fixtureを使うと、テスト関数から前処理・後処理を分離できて、シンプルで保守性の高いテストコードが書けます。
fixtureの前処理・後処理でよくやる処理例は以下の通り。
項目 | よくやる処理例 |
---|---|
前処理 | DBのセットアップ モック化 パラメータ化 |
後処理 | DBのクローズ処理 tempファイルの削除処理 |
fixtureの使い方
fixtureを使うには、@pytest.fixture()をメソッドの前に書きます。
import pytest
class TestFixtures():
@pytest.fixture()
def sample(self):
print("前処理")
yield
print("後処理")
def test_hoge(self, sample):
print(sample)
print("本処理")
assert 1 == 1
着目すべきはyieldです。以下のルールがあります。
- yieldの手前にあるコードは前処理として、実行
- yieldの後ろにあるコードは後処理として、実行
実行すると、以下のようになります。
$ pytest -s test_fixture_before_after.py
======================================================== test session starts ========================================================
platform darwin -- Python 3.10.6, pytest-7.1.3, pluggy-1.0.0
rootdir: (省略)
collected 1 item
test_fixture_before_after.py 前処理
None
本処理
.後処理
========================================================= 1 passed in 0.01s =========================================================
fixtureのスコープ
fixtureでは以下の通り、スコープを定義できます。
スコープ | 説明 |
---|---|
scope=’function’ | テスト関数ごとに、一回実行(デフォルト) |
scope=’class’ | テストクラスごとに、一回実行 |
scope=’module’ | モジュール(ファイル)ごとに、一回実行 |
scope=’package’ | パッケージごとに、一回実行 |
scope=’session’ | セッション(pytestコマンドでテストを実施)ごとに、一回実行。 |
スコープを明示的に指定しない場合、デフォルトのスコープはfunctionになります。
スコープの書き方
スコープの書き方は@pytest.fixture(scope=’XXX’)でスコープを指定します。
functionであれば、@pytest.fixture(scope=’function’)と書きます。
スコープの実行順序
では、スコープがどのように動くのか実際に見てみましょう。テストコードのサンプルは以下の通りです。
- fixtureのスコープは、function、class、module、sessionを指定
- テストモジュールは1つ
- テストクラスは2つ
- テスト関数は、合計4つ(テストクラスに2つずつ)
import pytest
@pytest.fixture(scope='function')
def fixture_function():
"""
テスト関数ごとに、一回実行
"""
print('前処理: function')
yield
print('後処理: function')
@pytest.fixture(scope='class')
def fixture_class():
"""
テストクラスごとに、一回実行
"""
print('前処理: class')
yield
print('後処理: class')
@pytest.fixture(scope='module')
def fixture_module():
"""
モジュール(ファイル)ごとに、一回実行
"""
print('前処理: module')
yield
print('後処理: module')
@pytest.fixture(scope='session')
def fixture_session():
"""
セッション(pytestコマンドでテストを実施)ごとに、一回実行
"""
print('前処理: session')
yield
print('後処理: session')
class TestFixtureScope_2():
def test_1(
self,
fixture_function,
fixture_class,
fixture_module,
fixture_session):
pass
def test_2(
self,
fixture_function,
fixture_class,
fixture_module,
fixture_session):
pass
class TestFixtureScope_1():
def test_1(
self,
fixture_function,
fixture_class,
fixture_module,
fixture_session):
pass
def test_2(
self,
fixture_function,
fixture_class,
fixture_module,
fixture_session):
pass
実際に実行した結果が、以下です。
$ pytest -vs test_fixture_scope.py
======================================================== test session starts ========================================================
platform darwin -- Python 3.10.6, pytest-7.1.3, pluggy-1.0.0 -- /Users/y5a1m5a/Documents/workspace/python-sample/pytest-demo/venv/bin/python3.10
cachedir: .pytest_cache
rootdir: (省略)
collected 4 items
test_fixture_scope.py::TestFixtureScope_2::test_1 前処理: session
前処理: module
前処理: class
前処理: function
PASSED後処理: function
test_fixture_scope.py::TestFixtureScope_2::test_2 前処理: function
PASSED後処理: function
後処理: class
test_fixture_scope.py::TestFixtureScope_1::test_1 前処理: class
前処理: function
PASSED後処理: function
test_fixture_scope.py::TestFixtureScope_1::test_2 前処理: function
PASSED後処理: function
後処理: class
後処理: module
後処理: session
========================================================= 4 passed in 0.03s =========================================================
スコープごとの前処理、後処理の呼び出し回数は以下の通りです。
スコープ | 呼び出し回数 |
---|---|
function | 4回 |
class | 2回 |
module | 1回 |
session | 1回 |
ここでスコープの動作について分かることは、以下の通りです。
- functionはテスト関数、classはテストクラス、moduleはファイルの数だけ実行
- sessionはテストを通じて、一回だけ実行
- 前処理は、スコープの広いsessionから、狭いfunctionの順で処理を実施
- 前処理は、スコープ狭いfunctionから、広いsessionの順で処理を実施
この特徴をしっかりと覚えて、スコープを定義すれば、とても便利にfixtureが使えます。
fixtureでパラメータ化する方法
一つの関数に対して、様々なデータセットで検証したい時は、パタメータ化テストがシンプルに書けるので、便利です。
pytestでのパラメータ化テストは3つの方法があります。
- テスト関数をパラメータ化:最もスタンダード
- fixtureをパラメータ化:前処理、後処理を書きたい時に利用
- フック関数(pytest_generate_tests)で、パラメータ化:複雑なデータセットを書きたい時に利用
fixtureを使ってパラメータ化することで、パラメータに応じた前処理・後処理を書くことができます。
少ないコードで様々なデータパターンのテストができるのがメリットですね。上手く使えば、生産性・品質向上して、周りの人の評価もあがりますね。
パラメータ化は現場でテストコード書くなら、絶対に利用します。必ずマスターして下さい。
以下にpytestでパラメータ化する方法について詳しく解説した記事があります。もちろんサンプルコードありです。
conftest.pyでfixtureを複数のテストファイルで共有する
複数のテストファイルでfixtureを共有したい時は、conftest.pyに定義すれば、簡単に共有ができます。
conftest.pyのサンプルは以下の通りです。
import pytest
@pytest.fixture(scope="session")
def conf():
print("conftest:前処理")
yield
print("conftest:後処理")
そして、実際に使ってみます。
conftest.pyを利用する時、import文は不要で、テスト関数の引数にfixtureを定義した関数を渡します。
def test_conf(conf):
pass
実行結果は、以下の通りです。
$ pytest -vs test_conf.py
======================================================== test session starts ========================================================
platform darwin -- Python 3.10.6, pytest-7.1.3, pluggy-1.0.0 -- /Users/y5a1m5a/Documents/workspace/python-sample/pytest-demo/venv/bin/python3.10
cachedir: .pytest_cache
rootdir: (省略)
collected 1 item
test_conf.py::test_conf conftest:前処理
PASSEDconftest:後処理
========================================================= 1 passed in 0.01s =========================================================
autouseを指定する
常に使うfixtureがある場合、fixtureの引数にautouse=Trueを指定すると便利です。
書き方は以下の通り。
@pytest.fixture(scope=”function”, autouse=True)
テストコードのサンプルは以下の通り。
import pytest
@pytest.fixture(scope="function", autouse=True)
def fix_autouse():
print("autouse")
def test_1():
pass
test_1関数の引数にfix_autouseがないとfixtureが動きませんが、autouse=Trueを指定しているので、引数なしでも動きます。
これを実行した結果が以下です。
$ pytest -vs test_autouse.py
======================================================== test session starts ========================================================
platform darwin -- Python 3.10.6, pytest-7.1.3, pluggy-1.0.0 -- /Users/y5a1m5a/Documents/workspace/python-sample/pytest-demo/venv/bin/python3.10
cachedir: .pytest_cache
rootdir: (省略)
collected 1 item
test_autouse.py::test_1 autouse
PASSED
========================================================= 1 passed in 0.00s =========================================================
前処理が実施されていることが分かります。
Python学習におすすめのコンテンツ
現役シリコンバレーエンジニアが教えるPython 3 入門 + 応用 +アメリカのシリコンバレー流コードスタイル
Udemyの講座でPythonなら、文句なしにおすすめ
- 分かりやすい言葉や具体的なコードで工夫して説明しているので、初心者でも理解できる。
- 初心者でも挫折しないように、丁寧に詳しく手順を説明している。
- 講座内で取り扱う課題は実践的なものが多く、書籍では身につかない応用力を身につけられる。
- 情報量が多いのに、講座の進み方が速いので、一回では理解が追いつかない。
- 課題の難易度が高すぎる場合がある。
- 講師のアクセントが特殊で、聞き取りにくいと感じたことがある。
テスト駆動Python 第2版
日本語で唯一、pytestだけに焦点を当てて、詳細に書かれた書籍です。
- 日本語でpytestの全てについて、解説されている唯一の書籍
- 段々とステップアップして、深くなるので、初心者でも、中級者でも、上級者でも、学びがある
- サンプルコードには、サンプルアプリが含まれているため、実践的に学べる工夫あり
- 章末にまとめがあり、復習の役に立つ
- 第7章に「どのようなテストを書くのか」についての解説があり、テストに対する考え方が深まる
- 後半になると、かなり高度な内容になるため、初心者には難しい
- タイトルに「テスト駆動開発」と書かれているが、該当部分の記述が少なく、テスト駆動開発を知りたい人は期待外れになるかも
間違えて、第1版を買わないように注意して下さい。
まとめ:fixtureを使えば、テストコードの幅が広がる
ここまで、fixtureの書き方を解説しました。
fixtureを有効活用することで、高度な前処理、後処理が短いコードで書けます。
前処理、後処理が書けると、テストコードの幅が広がります。それが品質の確保、向上に繋がるので、しっかりと使い方をマスターしましょう。
Pythonを勉強する時、何から勉強するか分からず、挫折します。初心者でも、中級者でも、レベルに合わせた勉強方法を分かりやすくまとめています。
コメント