0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

pytest フィクスチャの実行タイミングと返却値の参照方法のまとめ (scope, autouse 指定時)

Last updated at Posted at 2025-01-14

scope, autouse 両指定時の返却値の参照方法とそれに実行タイミングが影響を受けないかについて説明やサンプルスクリプトを書ききっている記事が見受けられなかったので、以下の「まとめ」にまとめ、サンプルスクリプトを書きました (仕様は pytest-8.3.4 現在です)。1

実行環境は Python 3.11.4, pytest-8.3.4 です。

まとめ

以下の 1. の記述は参考文献 1., 2. および手元での確認結果に基づいています。2. の記述については、ドキュメントには autouse=True フィクスチャの返却値の参照例がないため比較的手元での確認結果ベースです (i.e. 公式の想定する使用方法なのかは保証できません)。

  1. pytest のフィクスチャは原則として、テスト関数から引数で要求されたとき (あるいは要求された他フィクスチャから引数で要求されたとき)、その都度実行される2。ただし、
    1. autouse=True が指定された場合は、フィクスチャ視点、「そのフィクスチャの影響下にあるすべてのテスト関数」がそのフィクスチャを要求しているものとみなす (※)
    2. デフォルトの scope='function'より広い scope='session' のようなスコープが指定された場合は、スコープ内での最初の要求時に初めて実行され、2回目以降の要求時は再実行されずに実行済み結果が再利用され、もし後処理があれば (= yield フィクスチャであれば) スコープ内のすべてのテスト関数終了後に実行される3
  2. pytest のフィクスチャの return (yield) 値をテスト関数側から実際に参照したい場合、常に引数に明記する必要がある (フィクスチャ側に autouse=True を指定していたとしても、テスト関数視点では明示的に要求しなければならない;この明示的要求によってスコープ付きフィクスチャが余計に再実行されるようなことはない)。

(※) ここで、「そのフィクスチャの影響下にあるすべてのテスト関数」とは、そのフィクスチャが conftest.py に定義されている場合はそのディレクトリ階層以下にあるすべての test_*.py 内のテスト関数、そのフィクスチャが test_x.py のトップレベルに定義されている場合はそのファイル内のテスト関数、そのフィクスチャが class TestX: インスタンスメソッドとして定義されている場合はそのクラス内のテスト関数といった具合です。

参考文献

  1. How to use fixtures - pytest documentationautouse=True にしたときの返却値を参照するような例はありませんでした。
  2. Fixtures reference - pytest documentation#autouse-fixtures-are-executed-first-within-their-scopeautouse=True にしたときの実行順があります。冒頭の説明では説明を複雑にしないために省きましたが、「そのフィクスチャが要求される時」とは、テスト関数から直接要求される時だけでなく、他のフィクスチャから間接的に要求される時も含みます。

サンプルスクリプト

例えば全テスト実行前に環境変数を立てて、全テスト実行後に環境変数を削除したいとし、かつ、環境変数に立てた値を各テストから必要に応じて受け取りたいとします。このようなとき、以下の my_key のようなフィクスチャを使用すると思います。

test_0.py
# ----- ここから下は本来 conftest.py に分離する -----
# セッションスコープのフィクスチャは本来 conftest.py に書くべきである。
# さもないと、テストファイルが複数あったとき当該テストファイルのテスト実行時まで実行されない。
# つまり、scope='session' とあるのに実際にはそうならない。
# そのような、コードを名が体を表していない状態にすることは避けるべきである。

import pytest
import os

@pytest.fixture(scope='session', autouse=True)
def my_key():
    print('\n===== setup =====')
    assert os.environ.get('MY_KEY') is None
    os.environ['MY_KEY'] = 'hoge'
    yield os.environ['MY_KEY']
    print('\n===== teardown =====')
    os.environ.pop('MY_KEY')

# ----- ここから上は本来 conftest.py に分離する -----

def test_00():
    print('\n----- test_00 -----')

def test_01(my_key):  # 受け取りたいときは引数に明記する
    print('\n----- test_01 -----')
    assert my_key == 'hoge'

def test_02(my_key):  # 受け取りたいときは引数に明記する
    print('\n----- test_02 -----')
    assert my_key == 'hoge'

def test_03():
    print('\n----- test_03 -----')

pytest -s の実行結果は以下になります。

test_0.py
===== setup =====

----- test_00 -----
.
----- test_01 -----
.
----- test_02 -----
.
----- test_03 -----
.
===== teardown =====

フィクスチャに autouse=True を付けない場合、setup タイミングが最初の受け取り要求時にずれます。teardown タイミングが全テスト終了後であるのは変わりません。

test_0.py
----- test_00 -----
.
===== setup =====

----- test_01 -----
.
----- test_02 -----
.
----- test_03 -----
.
===== teardown =====

なお、scope=function にすると my_key の setup と teardown が各テストの前後で実行されるようになります。さらに autouse=True を外すと my_key を要求するテストの前後でのみ実行されるようになります (スクリプト略)。

また、上記のスクリプトとほぼ同じでフィクスチャ名だけ my_key_1 にリネームしたスクリプト test_1.py を追加すると、my_key_1test_1.py のテストの順番が回ってくるまで実行されません (スクリプト略)。これではセッションスコープになっておらず、conftest.py に切り分けるべきです (切り分けると解決します) (スクリプト略)。

  1. 最近久しぶりに pytest を書いたのですが、なぜかフィクスチャを autouse=True にしておけばテスト関数の引数に書かなくても返却値を参照できると思っていました。しかし、返却値を参照するならば明示的に引数に書く必要がありました。なお、引数に書かない場合は返却値ではなくフィクスチャ関数自体が参照され、そういわれるとそれはそうなるだろうと思います。ただ、なぜか参照できると思い込んでおり、Web 上にもあたかも参照できるような日本語記事があるので (ただ完全なスクリプトはない)、参照できた時期もあったのか存在しない記憶であったのかわかりません。

  2. フィクスチャ hoge をテスト関数の引数に指定して、そのテスト関数内で何度も hoge を参照する分にはその度に再実行はされません。参考文献 1. の Fixtures can be requested more than once per test (return values are cached) という見出しを参照してください。

  3. ただし、前処理内でエラーがあった場合 (Ex. 環境変数がまだ立っていないかを assert するべきなのに、誤ってもう立っているかを assert するなどすれば失敗し続けます)、セッションスコープを指定したのにテスト関数ごとに実行がチャレンジされ続けるはずです (実行済み結果がずっと存在しないのでこの動きになると思います)。このエラーをみると、あたかもスコープが壊れているのかと思えますが、壊れているのは自分が記述した判定なので注意が必要です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?