Python | テスト・設計・品質:pytest 基本

Python Python
スポンサーリンク

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_simple
test_add_zero
test_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 でテストできる。
pytestpytest test_xxx.pypytest test_xxx.py::test_yyy で、全部・一部・個別のテストを実行できる。

ここまでできれば、もう「pytest を使っている人」です。

タイトルとURLをコピーしました