ユニットテストしたい(pytest
)
$ pytest --version
pytest 8.3.3
$ pytest
$ pytest --verbose
$ pytest ファイル名
pytest
はPythonのユニットテスト群をまとめて実行できるツールです。
プロジェクトのルートディレクトリで実行すればテストをまとめて実行できます。
--verbose
オプションで、それぞれのテストごとに結果を表示できます。
インストールしたい(pytest
)
pipx
でインストール
$ pipx install pytest
poetry
でインストール
$ poetry add pytest --group=test
$ poetry add pytest-mock --group=test # モックを使ったユニットテスト
$ poetry add pytest-cov --group=test # カバレッジの計測
$ poetry add pytest-html --group=test # テスト結果をHTMLファイルに出力
poetry
で管理している場合は--group=test
に分類するとよいと思います。
uv
でインストール
$ uv tool install pytest
$ uv tool install pytest-mock
$ uv tool install pytest-cov
pipx
やuv
を使ってシステム(の仮想環境)にインストールできます。
テスト結果をHTMLファイルに出力する場合はpytest-html
のが必要です。
unittest.mockを使う場合は、
pytest-mock
もインストールしておくとよいです。
カバレッジを計測した場合はpytest-cov
が必要です。
ディレクトリ構造
$ cd プロジェクト
$ tree
.
├── 自作パッケージ名
│ ├── __init__.py
│ ├── 自作モジュール1.py
│ ├── 自作モジュール2.py
├── tests
│ ├── __init__.py
│ ├── test_自作モジュール1.py
│ ├── test_自作モジュール2.py
├── poetry.toml
├── pyproject.toml
ユニットテスト用のファイルは、tests
ディレクトリの中に作成します。
ファイル名の先頭はtest_
にします。
上のサンプルはpoetryで作成したディレクトリ構造です。
自作パッケージと同階層にtests
ディレクトリを作成し、
その中にユニットテストを作成しています。
1import pytest
2from unittest.mock import patch
3
4@patch("subprocess.run")
5def test_download(mock_subprocess_run):
6
7 """Test download method"""
8
9 # テスト用URL
10 url = TEST_SHARED_URL
11 sheet = Sheet(
12 url=url,
13 filename="output.csv")
14 sheet.download()
15
16 mock_subprocess_run.assert_called_with(
17 ["wget", "--quiet", "-O", "output.csv", sheet.export_url]
18 )
上のサンプルは、
sheet.download
の中で、
subprocess.run
を使って
wget
を呼んでいる場合のテストです。
subprocess.run
をモックすることで、wgetを実行せずにテストできるようにしています。
テスト関数の引数名はモック名にします。
この場合はmock_subprocess_run
でアクセスできるようになります。
wgetを実行していないため、filename="output.csv"
に設定したファイルは作成されません。
そのため、assert_called_with
を使って、指定した引数で関数が呼ばれたかどうかで、動作確認しています。
モックしたい
モック/パッチの作り方はまだわかっていないので、 ChatGPTに聞きながら書くことが多いです。
ファイル書き込みをモックしたい(pathlib.Path.write_text
)
1def 関数名(引数):
2 p = Path("ファイル名")
3 p.write_text("ファイルの内容", encoding="utf-8")
pathlib.Path.write_text
を使っている関数のユニットテストを作成したときのサンプルです。
関数名や引数名は適当に置き換えて読んでください。
1from unittest.mock import patch
2
3@path("pathlib.Path.write_text")
4def test_関数名(mock_write):
5 # test strings
6 text = "ファイル内容"
7
8 # run a function
9 関数名(引数)
10
11 # assertion
12 # write_textが1回だけ呼ばれたことを確認
13 mock_write.assert_called_once_with(text, encoding="utf-8")
pathlib.Path.write_text
をモックします。
write_text
は内部でpathlib.Path.open
を使っていますが、
mock_open
は必要ありません。
注釈
open
関数を使う場合はmock_open
が必要です。
例外をテストしたい(pytest.raises
)
1import pytest
2
3def test_関数名():
4 with pytest.raise(例外名):
5 関数(...) # <- 例外を発生させる
pytest.raise
で例外をテストできます。s
繰り返しテストしたい(@pytest.mark.parametrize
)
1@pytest.mark.parametrize(
2 "a, b, expected",
3 [ (1, 2, 3),
4 (3, 4, 5),]
5)
6def test_関数名(a, b, expected):
7 assert 関数名(a, b) == expected
@pytest.mark.parametrize
デコレータで、
異なる値で繰り返しテストできます。
テスト用の設定したい(@pytest.fixture
)
1@pytext.fixture
2def sample_data():
3 return [1, 2, 3]
4
5def test_data_length(sample_data):
6 assert len(sample_data) == 3
@pytest.fixture
でテスト用の設定値を作成できます。