pytestの基本的な使い方
2022 年最後の技術記事は pytest についてです。
Python を業務で使っていると、テストを書くことは必ずあると思います。
そんな時にまず候補に上がるのが pytest。
今回はそんな pytest の基礎的なところを書いていきます。
pytest を使う準備
まずはインストールします。例によって poetry で環境を作るのでpoetry add pytest
します。
以下は今回のテスト用のディレクトリ構成です。
.
├── poetry.lock
├── pyproject.toml
├── src
│ ├── __init__.py
│ ├── error.py
│ ├── module1.py
│ └── sandbox.ipynb
└── tests
├── __init__.py
└── test_module.py
pytest の設定ファイル
pytest を実行する際に必要に応じて使いたい設定を書いておきます。基本的な使い方のみならなくても動作します。
pytest では Python でよくある設定ファイル複数に対応しています。poetry を使っているならばpyproject.toml
に書くのが良いでしょう。単純に pytest を使いたいだけならpytest.ini
に書きます。
(参考:https://docs.pytest.org/en/latest/reference/customize.html)
よく使う設定だけ書いていきます。出来ることはたくさんあるので、公式サイトのCommand-line Flagsを参照ください。
また別途プラグインのインストールが必要ですが、よく使うのでカバレッジを計測するpytest-cov
のフラグも載せておきます。
フラグ | 説明 |
---|---|
-s | print 文 などを表示できるようになる |
-v | テストケース名、テストケースごとに PASSED や FAILED などの詳細情報が表示される |
-x | テストが失敗した時点でテストを終了する |
-m <maker名> | 指定した maker 名 の デコレータをつけたテストのみ実行される。デコレータは次のように書く@pytest.mark.<maker名> |
--durations=N | テストケースごとの実行時間、setup や teardown の時間を表示する。N=0 だと全てのテストケースに対して実行される |
--cov --cov-branch | 条件分岐のカバレッジ(C1)をみるためのフラグ |
これらを書いたpyproject.toml
は以下になります。duration
や-x
は適宜使うので設定ファイル上は一旦除いてます。
[tool.pytest.ini_options]
addopts = "--cov --cov-branch -s -v"
testpaths = [
"tests",
]
pytest で正常系をテストする場合
簡単な以下の関数をテストしてみます。
from src.error import NotIntError
def add_one(x: int) -> int:
if not isinstance(x, int):
raise NotIntError("int以外の型です。入力はintのみです")
return x + 1
後々異常系もテストしたいので、あえて例外を発生させてます。
そしてこれが正常系のテストコード。
from src.module1 import add_one
def test_正常系():
result = add_one(1)
expect = 2
assert result == expect
実行はターミナルでpytest
と打つだけでいけます。ファイルやクラスを指定して実行することもできます。
(.venv) MacBook-Pro pytest-check % pytest
====================================================================================================== test session starts ======================================================================================================
platform darwin -- Python 3.8.7, pytest-7.2.0, pluggy-1.0.0
rootdir: /Users/work/pytest-check
collected 1 item
tests/test_module.py . [100%]
======================================================================================================= 1 passed in 0.01s =======================================================================================================
これで正常系のテストができました。
pytest で異常系をテストする場合
続いて異常系です。以下が異常系テストです。
def test_異常系():
with pytest.raises(NotIntError):
_ = add_one("1") # strの数値を渡す
これで先ほど関数で定義しておいたNotIntError
が発生するので、pytest.raises で発生する例外を指定します。
pytest で複数パラメータで実施する
正常系のテストをかなり適当に書いていたのでもう少し整えてみます。 ここではまず複数のパラメータを試すようにします。
@pytest.mark.parametrize("val, expect", [(1, 2), (100, 101), (-1, 0)])
def test_正常系(val: int, expect: int):
result = add_one(val)
assert result == expect
パラメータを複数テストに渡すには@pytest.mark.parametrize
のデコレータを使います。
デコレータに渡すのは引数名と引数のペアのリストです。
pytest の fixture(フィクスチャー)
テストの事前準備と後処理(いわゆる setup, teardown)を実行してくれるのが fixture です。
よくデータベースに保存されたデータを前提とした処理などに使われます。
今回はもっと簡単にprint
でやってみます。
@pytest.fixture()
def start_print():
print("Start Test !!")
@pytest.mark.parametrize("val, expect", [(1, 2), (100, 101), (-1, 0)])
def test_正常系(start_print, val: int, expect: int):
result = add_one(val)
assert result == expect
実行すると以下のようになります。(見づらいので-v
は使わずに実行してます)
tests/test_module.py Start Test !!
.Start Test !!
.Start Test !!
..
たしかに print 文の内容が表示されています。 続いて teardown の処理を書いてみます。先ほどの fixture に ß 以下のように yield 句とその後に print 文を追加します。
@pytest.fixture()
def start_print():
print("Start Test !!")
yield
print("Finish Test")
以下が実行結果です。
tests/test_module.py Start Test !!
.Finish Test
Start Test !!
.Finish Test
Start Test !!
.Finish Test
.
テストケースの最後に yield 句の後の print 文が実行されていることがわかります。
まとめと次回に向けたメモ
今回は pytest の超基本的なところだけを記載しました。 実はまだまだ書きたいことがあるので、次回続きを書きたいと思います。
書くことメモ
- パラメータの複数組み合わせ方法
- fixtute から fixture を使う方法
- fixture にパラメータを渡す方法
- モックの使い方