1. pytestについて
ユニットテストを簡潔に書けるパッケージです
公式ドキュメント
https://docs.pytest.org/en/stable/
2. インストール
pip install pytest
3. 使う
3-1. 実行
pytestが入った環境でpytestを実行すると「test_*.py」となっているファイルすべてについてpytestを実行してくれます
pytest
ファイルを指定して実行することもできます
pytest test_1.py
3-2. テストの書き方
pyファイル内の関数名が「test_*」となっている関数をすべて順番に実行して、どの関数でエラーが発生したかを教えてくれます
def func(x):
return x + 1
def test_answer():
assert func(3) == 5
実行すると以下のように表示されます
> pytest
=================================================== test session starts ===================================================
platform win32 -- Python 3.12.1, pytest-8.3.3, pluggy-1.5.0
rootdir: C:\Users\jjaka\マイドライブ\python\20241009_pytest\code
plugins: anyio-4.3.0
collected 1 item
test1.py F [100%]
======================================================== FAILURES =========================================================
_______________________________________________________ test_answer _______________________________________________________
def test_answer():
> assert func(3) == 5
E assert 4 == 5
E + where 4 = func(3)
test1.py:7: AssertionError
================================================= short test summary info =================================================
FAILED test1.py::test_answer - assert 4 == 5
==================================================== 1 failed in 0.09s ====================================================
今回はエラーがでるようにfuncが書かれているので、後はテストを通るように修正するだけです
3-3. 複数テストを実行
1つのファイルに関数名が「test_*」である複数の関数を書いておくと順番にテストを実行してくれます
def func(x):
return x + 2
def test_type():
assert isinstance(func(3), int)
def test_answer1():
assert func(3) == 5
assert func(4) == 6
def test_answer2():
assert func(3) == 4
成功すると「.」、エラーが出ると「F」で表示して、エラーが出た場合にエラー発生場所とエラーコードを表示してくれます
> pytest test_1.py
=================================================== test session starts ===================================================
platform win32 -- Python 3.12.1, pytest-8.3.3, pluggy-1.5.0
rootdir: C:\Users\jjaka\マイドライブ\python\20241009_pytest\code
plugins: anyio-4.3.0
collected 3 items
test_1.py ..F [100%]
======================================================== FAILURES =========================================================
______________________________________________________ test_answer2 _______________________________________________________
def test_answer2():
> assert func(3) == 4
E assert 5 == 4
E + where 5 = func(3)
test_1.py:15: AssertionError
================================================= short test summary info =================================================
FAILED test_1.py::test_answer2 - assert 5 == 4
=============================================== 1 failed, 2 passed in 0.10s ===============================================
3-4. 複数ファイルを実行
複数のテストファイルを作っておけばまとめて実行してくれます
> pytest
=================================================== test session starts ===================================================
platform win32 -- Python 3.12.1, pytest-8.3.3, pluggy-1.5.0
rootdir: C:\Users\jjaka\マイドライブ\python\20241009_pytest\code
plugins: anyio-4.3.0
collected 3 items
test_1.py ..
test_2.py .
==================================================== 3 passed in 0.02s ====================================================
4. テストの書き方
4-1. 関数で書く
先ほどのように関数を実行してエラーが出るかテストします
def test_answer():
assert func(3) == 5
assert func(4) == 6
4-2. クラスで書く
一連のテストをクラスにまとめて書いて整理することができます
「Test」からはじまるクラスで、「test_」からはじまるメソッドが順番に実行されます
class TestClassDemoInstance:
value = 0
def test_one(self):
self.value = 1
assert self.value == 1
def test_two(self):
self.value = 2
assert self.value == 2
> pytest test_3.py
=================================================== test session starts ===================================================
platform win32 -- Python 3.12.1, pytest-8.3.3, pluggy-1.5.0
rootdir: C:\Users\jjaka\マイドライブ\python\20241009_pytest\code
plugins: anyio-4.3.0
collected 2 items
test_3.py ..
==================================================== 2 passed in 0.01s ====================================================
4-3. 例外処理をテストする
大抵のテストツールは例外を拾ってテストの失敗判定をするので、例外が狙い通り出たかテストするには別の仕組みが必要になります。pytestの場合はpytest.raises()を使うことで例外を受け取って狙い通りか確認することができます
以下の場合はExceptionGroupをraiseする関数f()を実行して、raiseされた内容が正しいかテストしています
import pytest
def f():
raise ExceptionGroup(
"Group message",
[
RuntimeError(),
],
)
def test_exception_in_group():
with pytest.raises(ExceptionGroup) as excinfo:
f()
assert excinfo.group_contains(RuntimeError)
assert not excinfo.group_contains(TypeError)
ExceptionGroupでなく普通の例外であっても同様にテストできます
def test_zero_division():
with pytest.raises(ZeroDivisionError):
1 / 0
assert excinfo.type is ZeroDivisionError
def test_recursion_depth():
with pytest.raises(RuntimeError) as excinfo:
def f():
f()
f()
assert "maximum recursion" in str(excinfo.value)
def test_foo_not_implemented():
def foo():
raise NotImplementedError
with pytest.raises(RuntimeError) as excinfo:
foo()
assert excinfo.type is RuntimeError
5. テスト用のディレクトリ
テスト関数の引数にtmp_pathを書くと、pytestがテスト用に一時的に準備したディレクトリを使う事ができます
以下のテストコードでなんというPATHのディレクトリが作成されるか確認できます
assert 0はエラーにして止める為に入れています
def test_needsfiles(tmp_path):
print(tmp_path)
assert 0
'PYTEST_TMPDIR/test_needsfiles0'というディレクトリが作成されてtmp_pathに渡されているので、print行で書きだされて確認できます。
F [100%]
================================= FAILURES =================================
_____________________________ test_needsfiles ______________________________
tmp_path = PosixPath('PYTEST_TMPDIR/test_needsfiles0')
def test_needsfiles(tmp_path):
print(tmp_path)
> assert 0
E assert 0
test_tmp_path.py:3: AssertionError
--------------------------- Captured stdout call ---------------------------
PYTEST_TMPDIR/test_needsfiles0
========================= short test summary info ==========================
FAILED test_tmp_path.py::test_needsfiles - assert 0
1 failed in 0.12s
一時ファイルの書きだしなどで利用すると終了後に消してくれます
まとめ
シンプルで使いやすくて見やすいので、すごく良いと思います
次はCI/CDで使ってみる予定です
以前doctestの使い方メモを作っていましたが、書き方と実行方法がかなり違っていて面白いと思います
https://qiita.com/studio_haneya/items/d44fc73781e88694cd3e
じつはunittestを使ったことが無いのでそちらも近々さわりたいと思ってます
参考