0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

pytestの使い方メモ

Posted at

1. pytestについて

ユニットテストを簡潔に書けるパッケージです

公式ドキュメント
https://docs.pytest.org/en/stable/

2. インストール

pip install pytest

3. 使う

3-1. 実行

pytestが入った環境でpytestを実行すると「test_*.py」となっているファイルすべてについてpytestを実行してくれます

terminal
pytest

ファイルを指定して実行することもできます

terminal
pytest test_1.py

3-2. テストの書き方

pyファイル内の関数名が「test_*」となっている関数をすべて順番に実行して、どの関数でエラーが発生したかを教えてくれます

test_1.py
def func(x):
    return x + 1

def test_answer():
    assert func(3) == 5

実行すると以下のように表示されます

terminal
> 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_*」である複数の関数を書いておくと順番にテストを実行してくれます

test_1.py
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」で表示して、エラーが出た場合にエラー発生場所とエラーコードを表示してくれます

terminal
> 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. 複数ファイルを実行

複数のテストファイルを作っておけばまとめて実行してくれます

terminal
> 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. 関数で書く

先ほどのように関数を実行してエラーが出るかテストします

test_1.py
def test_answer():
    assert func(3) == 5
    assert func(4) == 6

4-2. クラスで書く

一連のテストをクラスにまとめて書いて整理することができます
「Test」からはじまるクラスで、「test_」からはじまるメソッドが順番に実行されます

test_2.py
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
terminal
> 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された内容が正しいかテストしています

test_4.py
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でなく普通の例外であっても同様にテストできます

test_5.py
def test_zero_division():
    with pytest.raises(ZeroDivisionError):
        1 / 0
    assert excinfo.type is ZeroDivisionError
test_6.py
def test_recursion_depth():
    with pytest.raises(RuntimeError) as excinfo:

        def f():
            f()

        f()
    assert "maximum recursion" in str(excinfo.value)
test_7.py
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はエラーにして止める為に入れています

test_8.py
def test_needsfiles(tmp_path):
    print(tmp_path)
    assert 0

'PYTEST_TMPDIR/test_needsfiles0'というディレクトリが作成されてtmp_pathに渡されているので、print行で書きだされて確認できます。

terminal
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を使ったことが無いのでそちらも近々さわりたいと思ってます

参考

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?