キーワード: tmp_path
, monkeypatch.chdir
できること
テスト時は実際にファイルを参照せずに、ダミーファイルから参照させるようにする。
下記ディレクトリ構成で、 input/sample.csv
が通常実行時に参照されるファイル。テスト時には、参照しないようにする。
❯ tree -L 2 .
.
├── input
│ └── sample.csv
├── requirements.txt
├── src
│ └── app.py
└── tests
├── __init__.py
└── test_app.py
コード
import csv
import os
def main():
print(os.getcwd())
with open('input/sample.csv', 'r') as file:
reader = csv.reader(file)
for row in reader:
print(row)
if __name__ == '__main__':
main()
import pytest
import csv
import os
@pytest.fixture
def set_test_csv(tmp_path):
rows = [['Name', 'Age', 'City'],
['John', '30', 'New York'],
['Jane', '25', 'London']]
os.mkdir(tmp_path / 'input')
csv_file_name = tmp_path / 'input/sample.csv'
with open(csv_file_name, 'w', newline='') as file:
writer = csv.writer(file)
writer.writerows(rows)
@pytest.fixture
def main(tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
from src.app import main
return main
def test_app(tmp_path, set_test_csv, main):
main()
assert True
解説
app.pyの main
では、ハードコーディングで input/sample.csv
が指定されている。(こんなクソコードがないという話は置いておく)
test_app.pyの set_test_csv
でダミーのテストファイルを作成している。 tmp_path
(pytestの組込みfixture)という一時ディレクトリ配下に同じディレクトリを掘りファイル作成。
★ def main
の実装がポイント。
- fixtureとして定義
-
tmp_path
,monkeypath.chdir
でカレントディレクトリをtmp_path
に変更 - カレントディレクトリ変更後に、テスト対象のモジュールから関数をimport
main自体をfixtureにしているのは、 monkeypatch.chdir(tmp_path)
のロジックをimport前に差し込みたいため。
これより前にimport すると実際のファイルを参照してしまう。
test_app
のフローは、下記のような感じ。
-
set_test_csv
でダミーファイルを作成(fixtureの引数) -
main
でカレントディレクトリを一時ディレクトリに置き換えて、テスト対象関数のimport -
main()
で関数を実行。→このとき、ダミーファイルが参照される - 要件に沿って
assert
最後に
tmp_path_factory がよくわかってないので、場合によっては使うべきかも。
The tmp_path_factory is a session-scoped fixture which can be used to create arbitrary temporary directories from any other fixture or test.
notion AI改善バージョン
以下、notion AIに改善してもらったバージョン。
です・ます調になっただけかな??
できること
テスト時には、実際のファイルを参照せずに、ダミーファイルから参照させることができます。
下記ディレクトリ構成で、input/sample.csv
は通常実行時に参照されるファイルです。テスト時には、参照しないようにする必要があります。
❯ tree -L 2 .
.
├── input
│ └── sample.csv
├── requirements.txt
├── src
│ └── app.py
└── tests
├── __init__.py
└── test_app.py
コード
import csv
import os
def main():
print(os.getcwd())
with open('input/sample.csv', 'r') as file:
reader = csv.reader(file)
for row in reader:
print(row)
if __name__ == '__main__':
main()
import pytest
import csv
import os
@pytest.fixture
def set_test_csv(tmp_path):
rows = [['Name', 'Age', 'City'],
['John', '30', 'New York'],
['Jane', '25', 'London']]
os.mkdir(tmp_path / 'input')
csv_file_name = tmp_path / 'input/sample.csv'
with open(csv_file_name, 'w', newline='') as file:
writer = csv.writer(file)
writer.writerows(rows)
@pytest.fixture
def main(tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
from src.app import main
return main
def test_app(tmp_path, set_test_csv, main):
main()
assert True
解説
app.py
の main
では、ハードコーディングで input/sample.csv
が指定されています。(こんなクソコードがないという話は置いておきます)
test_app.py
の set_test_csv
でダミーのテストファイルを作成しています。 tmp_path
(pytestの組込みfixture)という一時ディレクトリ配下に同じディレクトリを掘り、ファイルを作成します。
★ def main
の実装がポイントです。
- fixtureとして定義
-
tmp_path
とmonkeypatch.chdir
でカレントディレクトリをtmp_path
に変更 - カレントディレクトリ変更後に、テスト対象のモジュールから関数をimport
main
自体をfixtureにしているのは、 monkeypatch.chdir(tmp_path)
のロジックをimport前に差し込みたいためです。
これより前にimport すると実際のファイルを参照してしまいます。
test_app
のフローは、下記のような感じです。
-
set_test_csv
でダミーファイルを作成(fixtureの引数) -
main
でカレントディレクトリを一時ディレクトリに置き換えて、テスト対象関数のimport -
main()
で関数を実行。→このとき、ダミーファイルが参照されます - 要件に沿って
assert
最後に
tmp_path_factory がよくわかっていないので、場合によっては使うべきかもしれません。
The tmp_path_factory is a session-scoped fixture which can be used to create arbitrary temporary directories from any other fixture or test.