@pytest.fixture 前処理、後処理に引数を渡す
すみません、少し長くなったので「前処理&後処理&引数を渡す」についてだけ、見たい場合「後処理でも引数を使いたい場合」の箇所に飛んでください。
各テストケースの前に共通の処理をしたい
そんなときに pytest で役立つのはご存知 fixture
@pytest.fixture
def setup():
# データの初期化など
table = Table()
return table
class TestClass:
def test_code_1(self, setup):
test_data = "test_data"
expected = "test_data"
assert test_data == expected # なんじゃこのテストはsetupいらんやんけ
こんな形で、共通化したい前処理を定義した関数 setup
に @pytest.fixture
をデコレートして使う。
テストを実行する関数 test_code_1
では、以下のように引数に追加するだけでテストの処理に入る前に setup
の内容を実行してくれる。
ちなみに、@pytest.fixture(autouse=True)
とすれば、 test_code_1()
の引数に、 setup
を渡さなくても実行される。
@pytest.fixture(autouse=True)
def setup():
...
class TestClass:
def test_code_1(self):
...
前処理に加えて、後処理も共通化したい
となったら
@pytest.fixture
def setup():
# 前処理
print("テストはじめるよ")
yield # return -> yield (戻り値があれば yield something)
# 後処理
print("テストおわったよ")
class TestClass:
def test_code_1(self, setup):
test_data = "test_code_1"
expected = "test_code_1"
print("test_code_1")
assert test_data == expected
def test_code_2(self, setup):
test_data = "test_code_2"
expected = "test_code_2"
print("test_code_2")
assert test_data == expected
上記のコードを実行すると、
テストはじめるよ
test_code_1
テストおわったよ
テストはじめるよ
test_code_2
テストおわったよ
と、各テストの前に setup
のyield
より前の部分、
各テストの後に setup
のyield
より後の部分を実行してくれる。
setup() の実行タイミングを変えるには(関数毎じゃなくて、テスト全体で一回とか)
1 つ前のコードは、setup 内の処理が、各テストケース毎に呼び出されているが、それは@pytest.fixture
に渡せるscope
という引数を指定していないから。
scope | 実行タイミング | default |
---|---|---|
function | テストケース毎 | ○ |
class | クラス毎 | |
module | テストファイル毎 | |
session | テスト全体で 1 回 |
上記がscope
で指定できる値で、デフォルトは function のため、指定しない場合はテストケース毎に実行される。
とここまでタイトルの、「引数を渡す方法」からいくらか脱線してきた気もしますが、ようやく本題に入ります。
setup() に引数を渡すには
まず、@pytest.fixture
をデコレートした関数に引数を渡す方法はこんな形です。
@pytest.fixture
def setup():
def _setup(arg):
# 前処理
return arg
return _setup
setup
の內部に、_setup
という関数を定義して、その中で前処理を記述します。そして、外側のsetup
では_setup
を返すようにしています。
setup() に引数を渡す
今までさんざん、@pytest.fixture
をデコレートして前処理、前処理言ってきましたが、前処理に限らず共通化したいことに使えるので、例えば引数の数字をソートする関数に@pytest.fixture
をつけて、引数を渡す例を書いてみます。
@pytest.fixture
def sort_desc():
def _sort_desc(arg1, arg2, arg3):
return sorted([arg1, arg2, arg3], reverse=True)
return _sort_desc
class TestClass:
def test_code_1(self, sort_desc):
test_data = sort_desc(1, 2, 3)
expected = [3, 2, 1]
assert test_data == expected
def test_code_2(self, sort_desc):
test_data = sort_desc(-1, 3, 100)
expected = [100, 3, -1]
assert test_data == expected
テストケースから呼び出すときには、_sort_desc
ではなく、sort_desc
を呼び出せば良いです。
前処理&後処理&引数を渡す
本当に最後に、共通の前処理、後処理をしたい、そして引数を渡したい場合について、サンプルコードを書いてみます。
先程出てきたクソサンプルコードを再掲
@pytest.fixture
def setup():
# 前処理
print("テストはじめるよ")
yield # return -> yield (戻り値があれば yield something)
# 後処理
print("テストおわったよ")
class TestClass:
def test_code_1(self, setup):
test_data = "test_code_1"
expected = "test_code_1"
print("test_code_1")
assert test_data == expected
def test_code_2(self, setup):
test_data = "test_code_2"
expected = "test_code_2"
print("test_code_2")
assert test_data == expected
前処理で”テストはじめるよ”と出力しているので、引数を渡して、テストケース名も出力するようにします。
@pytest.fixture
def setup():
# 內部に引数を持つ関数を追加
def _setup(arg):
# 前処理
print(f"テストはじめるよ test名: {arg}")
return
yield _setup
# 後処理
print("テストおわったよ")
class TestClass:
def test_code_1(self, setup):
setup("test_code_1")
test_data = "test_code_1"
expected = "test_code_1"
assert test_data == expected
def test_code_2(self, setup):
setup("test_code_2")
test_data = "test_code_2"
expected = "test_code_2"
assert test_data == expected
これで、今何のテストを実行しているかわかるようになりました。
めでたしめでたし
後処理でも引数を使いたい場合
後処理でも引数を使いたい場合は
@pytest.fixture
def setup():
args = [] # 後処理で使う引数を格納するリスト
def _setup(arg1):
# 前処理
args.append(arg1) # 引数をリストに格納
print(f"テストはじめるよ test名: {arg1}")
return
yield _setup
# 後処理
for arg in args:
print(f"テストおわったよ test名: {arg}")
もっと良い方法がありそうですが、とりあえずこれで実現できるはずです。
まとめ
今更ながら、後処理もするのに関数名がsetup
っていけてないですよね。
そしてサンプルコードが総じてひどくて申し訳ないです。が抽象化して、scope
もうまく活用してやって頂けたら幸いです。
そして、困ったら(いや最初から)公式ドキュメントを読んでください。
読んでくれてありがとうございました。
Discussion