pytestの使い方
「pytest」の使い方をまとめました。
1. pytest
「pytest」はPythonスクリプトをテストするためのフレームワークです。
2. インストール
Python 3.7以降で、以下のコマンドでインストールします。
$ pip install -U pytest pytest-mock pytest-freezegun
3. はじめてのテストの実行
はじめてのテストの実行の手順は、次のとおりです。
(1) Pythonスクリプト「test_sample.py」を作成し、以下のように編集。
・test_sample.py
def func(x):
return x + 1
def test_answer():
assert func(3) == 5
(2) テストを実行。
$ pytest -q
F [100%]
============================================================================ FAILURES =============================================================================
___________________________________________________________________________ test_answer ___________________________________________________________________________
def test_answer():
> assert func(3) == 5
E assert 4 == 5
E + where 4 = func(3)
test_sample.py:6: AssertionError
===================================================================== short test summary info =====================================================================
FAILED test_sample.py::test_answer - assert 4 == 5
1 failed in 0.03s
今回は、func(3)が5を返さなかったため、FAILEDが報告されています。
◎ テストの実行コマンド
テストの実行コマンドは、次のとおりです。-qで出力を簡潔にしています。
◎ テストの実行対象
テストの実行対象となるスクリプト名は「test_*.py」「*_test.py」です。
test_sample.py
テストの実行対象となるメソッド名は「test_*()」です。
def test_answer():
〜テスト〜
テストの実行対象となるクラス名は「Test*」です。
class TestClass:
def test_one(self):
〜テスト〜
def test_two(self):
〜テスト〜
4. assert
「assert」は、特定の条件を満たすかどうかを検証します。
使用例は、次のとおりです。
・test_assert.py
def func(x):
return x + 1
def test_answer():
assert func(3) == 5
5. raises
「raises」は、特定の例外が発生したかどうかを検証します。
使用例は、次のとおりです。
・test_raises.py
import pytest
def f():
raise SystemExit(1)
def test_mytest():
with pytest.raises(SystemExit):
f()
6. mark
「mark」は、テストにメタデータを付与することができます。
◎ テストのスキップ
@pytest.mark.skipを付与することで、テストをスキップできます。
・test_mark1.py
import pytest
def func(x):
return x + 1
@pytest.mark.skip
def test_answer():
assert func(3) == 5
◎ テストのパラメータに値を提供
@pytest.mark.parametrizeを付与することで、テストのパラメータに値を提供できます。これによって、様々な値セットでテストできます。
・test_mark2.py
import pytest
@pytest.mark.parametrize("x,y,sum", [
(1, 2, 3),
(4, 5, 6),
])
def test_add(x, y, sum):
assert (x + y) == sum
7. fixture
「fixture」を使うことで、テストのパラメータにオブジェクトを提供したり、テストに前処理や後処理を追加したりできます。
◎ テストのパラメータにオブジェクトを提供
関数に@pytest.fixtureを付与することで、テストのパラメータで提供するオブジェクト「fixture」として定義できます。テストでこの関数と同じ名前のパラメータを使用することで、fixtureを提供できます。
使用例は、次のとおりです。
・test_fixture1.py
import pytest
class MyDatabase():
def open(self):
print("open")
def update(self):
print("update")
def close(self):
print("close")
@pytest.fixture()
def my_database():
return MyDatabase()
def test_sample(my_database):
my_database.open()
my_database.update()
my_database.close()
pytestでprint()を出力するには、-sを指定します。
$ pytest -q -s test_fixture1.py
open
update
close
.
1 passed in 0.00s
◎ fixtureの関数に前処理と後処理を追加
fixtureの関数内で前処理と後処理を追加することもできます。
・test_fixture2.py
import pytest
class MyDatabase():
def open(self):
print("open")
def update(self):
print("update")
def close(self):
print("close")
@pytest.fixture()
def my_database():
# 前処理
my_database = MyDatabase()
my_database.open()
# テスト
yield my_database
# 後処理
my_database.close()
def test_sample(my_database):
my_database.update()
$ pytest -q -s test_fixture2.py
open
update
.close
1 passed in 0.01s
◎ クラスのテストに前処理を追加
@pytest.mark.usefixturesでクラスのテストに前処理を追加できます。
・test_fixture3.py
import pytest
@pytest.fixture()
def sample_fixture():
print("sample_fixture")
@pytest.mark.usefixtures('sample_fixture')
class TestSample(object):
def test_sample1(self):
print("test_sample1")
def test_sample2(self):
print("test_sample2")
$ pytest -q -s test_fixture3.py
sample_fixture
test_sample1
.sample_fixture
test_sample2
◎ グローバルで使うfixtureの定義
グローバルで使うfixtureを定義は、conftest.pyに記述します。
・conftest.py
import pytest
@pytest.fixture()
def sample_fixture():
print("sample_fixture")
・test_fixture4.py
import pytest
@pytest.mark.usefixtures('sample_fixture')
class TestSample(object):
def test_sample1(self):
print("test_sample1")
def test_sample2(self):
print("test_sample2")
$ pytest -q -s test_fixture4.py
sample_fixture
test_sample1
.sample_fixture
test_sample2
8. Mock
Mockは、ユニットテストを円滑に進めるため、関数やオブジェクトを代理させるのモジュールになります。
テストのパラメータにmockerを記述し、mocker.patch("<関数名>", return_value=<戻り値>)を呼ぶことで、任意の関数の戻り値変更できます。
・sample.py (テスト対象)
import requests
def sample1(url):
return check_url(url)
def check_url(url):
try:
response = requests.get(url)
return response.status_code
except Exception:
return 0
・test_mock.py (テスト)
from sample import sample1
def test_sample1():
assert sample1("https://www.google.com/") == 200
def test_sample2(mocker):
mocker.patch("sample.check_url", return_value=404)
assert sample1("https://www.google.com/") == 404
9. freezegun
datetime.now()をMock化したい場合も多いと思いますが、組み込み関数のためmockerではMock化できません。freezegunを使うことで可能になります。
@pytest.mark.freeze_time()でdatetime.now()をMock化します。
import datetime
import pytest
@pytest.mark.freeze_time(datetime.datetime(2022, 1, 1, 0, 0))
def test_datetime_now():
now = datetime.datetime.now()
assert now == datetime.datetime(2022, 1, 1, 0, 0)
この記事が気に入ったらサポートをしてみませんか?