テストコード

なぜテストコードを書くのか

コードをメンテナンス、管理していく上で、テストコードは威力を発揮する。テストコードを一度書いておくと、機能の追加、修正をした際に他の機能に影響がないかをテストコードを実行するだけで簡単に確認できる。また、シンプルに書かれたテストコードは生きたドキュメント(仕様書)として、読み手の理解を助けてくれる。

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 ======================================= 

参考資料


著者画像

ゆうき

2018/04からITエンジニアとして活動、2021/11から独立。主な使用言語はPython, TypeScript, SAS, etc.