pytest って何?まずはゴールのイメージから
pytest は、Python のテストを書くための「超定番ツール」です。
一言でいうと、
テスト用のファイルと関数を書いておくpytest コマンドを叩く
勝手に全部探して実行してくれる
という「テストランナー」です。
初心者がまず押さえるべきポイントは、とてもシンプルです。
テストファイルの名前のルール
テスト関数の書き方assert をどう書くか
実行方法(pytest)
ここから一緒に、最小限のところをコード付きで固めていきます。
いちばん小さい pytest の例から始める
テスト対象の関数を用意する
まずは、テストしたい関数を 1 個だけ用意します。calc.py というファイルを作って、こう書きます。
# calc.py
def add(a: int, b: int) -> int:
return a + b
Pythonやっていることは単純な足し算です。
でも、あえてこれをテストしてみます。
テストファイルを作る(名前のルールが大事)
pytest は、「ファイル名」と「関数名」のルールでテストを自動検出します。
テストファイル名は test_*.py または *_test.py
テスト関数名は test_ で始まる
このルールさえ守れば、あとは pytest が勝手に見つけてくれます。
test_calc.py というファイルを作って、こう書きます。
# test_calc.py
from calc import add
def test_add_simple():
assert add(1, 2) == 3
def test_add_zero():
assert add(0, 5) == 5
def test_add_negative():
assert add(-1, 3) == 2
Pythonここで重要なのは、assert です。
「こうなっていてほしい」という条件を書くと、
pytest がそれを評価して、成功か失敗かを判定してくれます。
実行してみる
ターミナルで、この 2 ファイルがあるディレクトリに移動して、こう打ちます。
pytest
すると、pytest が
test_*.py を探す
その中の test_ で始まる関数を全部実行するassert が全部 True なら成功、False になったらそのテストは失敗
という流れで動きます。
成功すると、だいたいこんな表示になります。
collected 3 items
test_calc.py ... [100%]
3 passed in 0.02s
この「最小セット」が、pytest の基本形です。
assert の書き方をしっかり理解する
assert は「期待する結果」を書く場所
pytest では、特別なアサート関数を覚える必要はありません。
Python 標準の assert をそのまま使います。
assert add(1, 2) == 3
Pythonここでやっているのは、
add(1, 2) == 3 を評価する
True なら何も起きない(テスト成功)
False なら AssertionError が起きる(テスト失敗)
というシンプルな動きです。
pytest は、この AssertionError をキャッチして、
「どのテストがどんな条件で失敗したか」をきれいに表示してくれます。
失敗したときの表示を見てみる
わざと失敗させてみましょう。test_add_simple をこう変えます。
def test_add_simple():
assert add(1, 2) == 4 # わざと間違える
Pythonもう一度 pytest を実行すると、こんな感じの出力になります。
> assert add(1, 2) == 4
E assert 3 == 4
E + where 3 = add(1, 2)
ここで注目してほしいのは、
左側が実際の値(3)
右側が期待した値(4)
として表示されていることです。
pytest は assert の式を解析して、
「何がどう違ったか」を人間に分かりやすく見せてくれます。
この「失敗したときの情報量」が、pytest の大きな強みです。
テストケースを増やすときの考え方
「1 テスト関数=1 つの観点」が基本
さっきの test_calc.py では、こう分けました。
test_add_simpletest_add_zerotest_add_negative
1 つのテスト関数で、1 つの観点をチェックするイメージです。
例えば、足し算の関数なら、
普通の正の数同士
0 を含む場合
負の数を含む場合
など、「気になるパターン」をそれぞれ別のテスト関数にしておくと、
どのパターンで壊れたかがすぐに分かります。
1 つのテスト関数に詰め込みすぎない
こんな書き方もできますが、
def test_add_all():
assert add(1, 2) == 3
assert add(0, 5) == 5
assert add(-1, 3) == 2
Pythonこの場合、最初の assert で失敗したら、
後ろの 2 つは実行されません。
また、「どのパターンが落ちたか」がテスト名から分かりにくくなります。
初心者のうちは、
テスト関数名で「何をテストしているか」が分かるようにする
1 関数に 1〜数個の assert にとどめる
くらいの感覚で分けておくと、あとで読みやすくなります。
例外が起きることをテストする(重要ポイント)
「エラーになること」も立派な仕様
関数によっては、「この入力のときはエラーを出すべき」という仕様があります。
例えば、0 で割ったらエラーにしたい関数を考えます。
# calc.py
def div(a: int, b: int) -> float:
if b == 0:
raise ValueError("b must not be zero")
return a / b
Pythonこのとき、「b が 0 のときに ValueError が出ること」をテストしたくなります。
pytest.raises を使う
pytest では、pytest.raises を使って「例外が起きること」をテストできます。
# test_calc.py
import pytest
from calc import div
def test_div_ok():
assert div(6, 3) == 2
def test_div_zero():
with pytest.raises(ValueError) as excinfo:
div(1, 0)
assert "b must not be zero" in str(excinfo.value)
Pythonここでやっていることは、
with pytest.raises(ValueError) のブロック内で
ValueError が発生することを期待する
発生しなければテスト失敗
発生したら、その例外オブジェクトを excinfo.value から取り出せる
という流れです。
「エラーが起きないこと」だけでなく、
「正しいエラーが起きること」もテストできるのが、とても大事なポイントです。
テストの実行パターンと便利なオプション
特定のファイル・テストだけを実行する
テストが増えてくると、「全部じゃなくて一部だけ実行したい」場面が出てきます。
特定のファイルだけ:
pytest test_calc.py
特定のテスト関数だけ:
pytest test_calc.py::test_add_simple
こうすると、開発中の関数に関係するテストだけを素早く回せます。
詳しく出力したいときは -v
テスト名を詳しく見たいときは、-v オプションを付けます。
pytest -v
すると、こんな感じで 1 テストごとに行が出ます。
test_calc.py::test_add_simple PASSED
test_calc.py::test_add_zero PASSED
test_calc.py::test_add_negative PASSED
どのテストが通っているか・落ちているかが一目で分かります。
pytest を使うときに意識してほしい「考え方」
「テストを書く=未来の自分を助ける」
pytest の書き方自体はすぐ覚えられます。
大事なのは、「何をテストするか」という考え方です。
バグを見つけたら、そのバグを再現するテストを書く
直したあと、そのテストが通ることを確認する
同じバグが将来また出ても、テストが守ってくれる
というサイクルを回せるようになると、
テストは「めんどくさい作業」から「安心をくれる仕組み」に変わります。
小さくてもいいから、とにかく 1 本書いてみる
最初から完璧なテストを書く必要はありません。
まずは、
1 つの関数
1 つのテストファイル
1 つの assert
だけでもいいので、「テストを書いて pytest を回す」という体験をするのが大事です。
そこから、
パターンを増やす
例外パターンもテストする
DB や API を使うコードのテストに広げる
と、少しずつ範囲を広げていけば十分です。
まとめ(pytest 基本の「型」を自分のものにする)
pytest の基本を、初心者目線で整理するとこうなります。
テストファイルは test_*.py、テスト関数は test_ で始める。assert に「期待する結果」を書くだけで、pytest が成功・失敗を判定してくれる。
例外が起きることは pytest.raises でテストできる。pytest、pytest test_xxx.py、pytest test_xxx.py::test_yyy で、全部・一部・個別のテストを実行できる。
ここまでできれば、もう「pytest を使っている人」です。
