テストコード
なぜテストコードを書くのか
コードをメンテナンス、管理していく上で、テストコードは威力を発揮する。テストコードを一度書いておくと、機能の追加、修正をした際に他の機能に影響がないかをテストコードを実行するだけで簡単に確認できる。また、シンプルに書かれたテストコードは生きたドキュメント(仕様書)として、読み手の理解を助けてくれる。
Pythonでテストコードを書く際はpytestを使うと簡単にログを出力できる。
pytestの基本的な使い方
テストコードを書くために、まずpytestをインストールしておく。
> pip install pytest
以下のようなファイル構成で設定ファイル群を作成していく。
venv_pytest
├ src
| ├ __init__.py
| └ main.py
└ tests
├ __init__.py
└ test_main.py
これらファイル群の内容は以下のように記載する。
# src/__init__.py
# src/main.py
def add(a, b):
return a + b
# tests/__init__.py
# tests/test_main.py
from src import main
def test_add():
assert main.add(1, 2) == 3
上記のようにテスト対象のファイル名と関数名は先頭にtest_を付けておく必要がある。ここで、pytestというコマンドを実行すると、次のように出力されてテストが成功したことがわかる。
> pytest
==================================== test session starts ====================================
platform win32 -- Python 3.11.5, pytest-7.4.4, pluggy-1.0.0
rootdir: C:\Users\yuki\venv_pytest
plugins: anyio-4.3.0
collected 1 item
tests\test_main.py . [100%]
===================================== 1 passed in 0.02s =====================================
次に、test_main.pyに以下のようなassert文を追加してみる。
# tests/test_main.py
from src import main
def test_add():
assert main.add(1, 2) == 3
assert main.add(0, 0) == 1
再びpytestコマンドを実行すると、次のように出力されてテストが失敗したことがわかる。
> pytest
====================================== test session starts ======================================
platform win32 -- Python 3.11.5, pytest-7.4.4, pluggy-1.0.0
rootdir: C:\Users\yuki\venv_pytest
plugins: anyio-4.3.0
collected 1 item
tests\test_main.py F [100%]
=========================================== FAILURES ============================================
___________________________________________ test_add ____________________________________________
def test_add():
assert main.add(1, 2) == 3
> assert main.add(0, 0) == 1
E assert 0 == 1
E + where 0 = <function add at 0x000001B75326D440>(0, 0)
E + where <function add at 0x000001B75326D440> = main.add
tests\test_main.py:8: AssertionError
==================================== short test summary info ====================================
FAILED tests/test_main.py::test_add - assert 0 == 1
======================================= 1 failed in 0.17s =======================================
出力結果をみれば、どこでどのようにテストが失敗したのかが記載されており、どのように修正するべきかが容易にわかるようになっている。
今回の場合、テストコード側の単なる入力ミスなので次のように修正すれば、再びテストが成功するようになる。
# tests/test_main.py
from src import main
def test_add():
assert main.add(1, 2) == 3
assert main.add(0, 0) == 0
次に、以下のようにファイルを追加してみる。
venv_pytest
├ src
| ├ __init__.py
| ├ main.py
| └ sub.py ←ファイルを追加
└ tests
├ __init__.py
├ test_main.py
└ test_sub.py ←ファイルを追加
# src/sub.py
def subtract(a, b):
return a - b
# tests/test_sub.py
from src import sub
def test_subtract():
assert sub.subtract(1, 2) == -1
このようにファイルが複数ある場合も、これまで同様pytestコマンドを実行すると、次のように出力されてテストが成功したことがわかる。
====================================== test session starts ======================================
platform win32 -- Python 3.11.5, pytest-7.4.4, pluggy-1.0.0
rootdir: C:\Users\yuki\venv_pytest
plugins: anyio-4.3.0
collected 2 items
tests\test_main.py . [ 50%]
tests\test_sub.py . [100%]
======================================= 2 passed in 0.03s =======================================
なお、pytestコマンドの後ろに特定のファイルを指定すると、そのファイル内のテストのみを実行できる。
> pytest tests/test_sub.py
====================================== test session starts ======================================
platform win32 -- Python 3.11.5, pytest-7.4.4, pluggy-1.0.0
rootdir: C:\Users\yuki\venv_pytest
plugins: anyio-4.3.0
collected 1 item
tests\test_sub.py . [100%]
======================================= 1 passed in 0.03s =======================================
また、次のようにファイルを変更すると、例外が発生することを確認するテストも実行できる。
# src/sub.py
def subtract(a, b):
if not isinstance(a, int):
raise Exception("引数aがint型ではありません。")
if not isinstance(b, int):
raise Exception("引数bがint型ではありません。")
return a - b
# tests/test_sub.py
from src import sub
import pytest
def test_subtract_normal():
assert sub.subtract(1, 2) == -1
def test_subtract_abnormal():
with pytest.raises(Exception) as e:
sub.subtract(1, "2")
assert str(e.value) == "引数bがint型ではありません。"
pytestコマンドを実行すると次のように出力され、テストが成功したことが確認できる。
> pytest tests/test_sub.py
====================================== test session starts ======================================
platform win32 -- Python 3.11.5, pytest-7.4.4, pluggy-1.0.0
rootdir: C:\Users\yuki\venv_pytest
plugins: anyio-4.3.0
collected 2 items
tests\test_sub.py .. [100%]
======================================= 2 passed in 0.03s =======================================