こんにちは、ロジカル・アーツの笹原です。
今回は、VS Code と pytest を使用した Python コードの単体テストの方法を紹介したいと思います。
はじめに
VS Code ではテストの実行はもちろんですが、テストに対してデバッグも行うことができます。さらに、拡張機能を使用してカバレッジを表示することも可能です。以下では、pytest を使用した基本的なテスト方法と、デバッグ、カバレッジの表示方法を取り上げます。
以下の手順で扱う環境は次の通りです:
- OS は Windows 10
- Python のバージョンは 3.7.4
- VS Code のバージョンは 1.71.2
- Python の拡張パッケージをインストール済み
単体テスト
この記事では pytest を使用します。以下のコマンドでインストールします*1。
pip install pytest
テスト用サンプルコード
以下のコードをサンプルとして使用します。これを collatz.py
としてワークスペース直下に作成します。
この項の以下の説明は VSCode で Python の開発環境を用意する【デバッグ編】 - Blogical にあるものとまったく同じです。既に読まれている方は読み飛ばしてもらって大丈夫です。
collatz.py
def collatz(n): """与えられた数を初期値として Collatz 数列を計算する。 Args: n (int): 初期値。 Raises: NotPositiveNumberError: 初期値が0以下の場合に発生する。 Returns: tuple: 数列の長さと最大値のタプル。 """ seq = [n] if n <= 0: raise NotPositiveNumberError while n > 1: if is_even(n): n //= 2 else: n = 3 * n + 1 seq.append(n) return len(seq), max(seq) def is_even(n): """与えられた数が偶数かどうか判定する。 Args: n (int): 整数。 Returns: bool: 偶数であれば `True` を返す。 """ return n % 2 == 0 class NotPositiveNumberError(Exception): """正の整数でない例外クラス。""" pass if __name__ == "__main__": n = input("数字を入力してください:") try: length, max_num = collatz(int(n, base=10)) print(f"数列の長さ:{length}") print(f"数列の最大値:{max_num}") except (NotPositiveNumberError, ValueError): print("正の整数を入力してください。")
このサンプルコードでは Collatz 予想に出てくるアルゴリズムを実装してます。すなわち、与えられた正の整数に対して偶数であれば 2 で割り、奇数であれば 3 倍して 1 を足すという操作を繰り返し行います*2。
コード中の collatz
関数は入力が 1 に到達したときの数列の長さと、取り得る値の最大値を返します。
例えば、入力が 13 のとき Collatz 数列は
となり、数列の長さは 10、最大値は 40 です。
設定
VS Code で collatz.py
を開いておきます。すると以下の画像のようなフラスコアイコンが表示されると思うのでクリックし、「Configure Python Tests」をクリックします。
「pytest」を選択します。
「. Root directory」を選択します。
すると、settings.json
ファイルに以下の内容が追記されていると思います。
.vscode/settings.json
{ "python.testing.pytestArgs": [ "." ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true }
テストファイルの作成
次に、collatz.py
のテストファイル test_collatz.py
を作成します。テストは後で書くことにして、テスト用の関数だけ用意しておきます。
それぞれ is_even
、collatz
関数のテスト用関数です。
test_collatz.py
def test_is_even(): pass def test_collatz(): pass
設定がうまくいっていれば、テストエクスプローラーにテスト一覧が表示されます。
実行
まずは test_is_even
関数のテストから実装してみます。
is_even
関数は、引数 n
が偶数であれば True
を、奇数であれば False
を返します。よって、n
が偶数のとき True
を、奇数のとき False
を返すことをそれぞれテストすればよいことになります。
pytest の pytest.mark.parametrize
を使用することで複数パターンのテストを一つのテスト関数でできるので、これを活用して偶数の場合と奇数の場合のテストを test_is_even
関数で行います。
まず、test_collatz.py
ファイルの先頭に必要な import
文を追加した上で、test_is_even
関数を以下のように編集してください。
import pytest from collatz import collatz, is_even @pytest.mark.parametrize("n,expected", [(1, False), (2, True)]) def test_is_even(n, expected): assert is_even(n) == expected
簡単に解説すると、第一引数にはテスト関数に渡すパラメータ名をカンマ区切りで指定できます。ここでは n
と expected
としています。これが test_is_even
関数の引数の n
、expected
に対応しているわけです。
第二引数では、パラメータに渡す値のペアのリストを渡しています。ここでは、n=1,expected=False
と n=2,expected=True
がテスト関数に渡される値になります。つまり、テストでは is_even(1)
が False
、is_even(2)
が True
であることを確かめています。
それでは実行してみましょう。 関数のテストは、ガターにある緑の実行アイコンをクリックすることで実行できます。
テストが実行され、成功すると緑色のチェックが付きます。
テストが失敗した場合は、赤色のバツアイコンが表示されます。
test_collatz
の方も実装しましょう。こちらは collatz
関数の戻り値が 2 つあることに注意してください。
@pytest.mark.parametrize( "n,expected_len,expected_max_val", [(13, 10, 40), (28, 19, 52)] ) def test_collatz(n, expected_len, expected_max_val): len_, max_val = collatz(n) assert len_ == expected_len assert max_val == expected_max_val
デバッグ
テストのデバッグができるので、簡単に紹介します。
以下の画像のように、test_collatz
関数内の 1 行目にブレークポイントを打ち、テストエクスプローラーで当該関数のデバッグアイコンをクリックします。
ブレークポイントで停止します。
デバッグの基本的な使い方については、よければ以下の記事を参照してください。
カバレッジ
VS Code でカバレッジを表示することができます。今回必要なのは、pytest でカバレッジを取得するプラグイン pytest-cov と カバレッジを表示する VS Code の拡張機能 Coverage Gutters の二つです。
インストール
pytest-cov
は pip install pytest-cov
でインストールできます。Coverage Gutters については省略します。
設定
settings.json
の python.testing.pytestArgs
の中身を以下のように編集してください。
.vscode/settings.json
"python.testing.pytestArgs": [ "--cov=.", "--cov-report", "xml" ],
また、好みではありますが、同じく settings.json
に以下の設定を追記します。
"coverage-gutters.showGutterCoverage": false, "coverage-gutters.showLineCoverage": true
この設定では、カバレッジのガター(ブレークポイントを打つ場所)での表示を無効化し、ファイル行でのカバレッジの表示を有効化しています。 前者を非表示にしているのは、ガタ―でカバレッジが表示されている箇所にはブレークポイントが打てなくなるためです*3。
カバレッジの取得対象から除外する
上記の設定だと、テストファイルに対してもカバレッジが取得されます。テストファイルをカバレッジの取得対象から外すためには .coveragerc
ファイルをワークスペース直下に作成し、以下のように記述します。
.coveragerc
[run] omit = test_*.py
補足:デバッガとの競合
pytest-cov
を使用すると、テストのデバッグ時にブレークポイントで停止しないという問題が発生します。
Note If you have the pytest-cov coverage module installed, VS Code doesn't stop at breakpoints while debugging because pytest-cov is using the same technique to access the source code being run. To prevent this behavior, include --no-cov in pytestArgs when debugging tests, for example by adding "env": {"PYTEST_ADDOPTS": "--no-cov"} to your debug configuration. (See Debug Tests above about how to set up that launch configuration.) (For more information, see Debuggers and PyCharm in the pytest-cov documentation.) doch
引用:https://code.visualstudio.com/docs/python/testing#_pytest-configuration-settings
上の引用文を参考に、デバッグの設定に次を追記することで対処できます。
"purpose": ["debug-test"], "env": {"PYTEST_ADDOPTS": "--no-cov"}
例
.vscode/launch.json
{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Python: 現在のファイル", "type": "python", "request": "launch", "program": "${file}", "console": "integratedTerminal", "justMyCode": true, "purpose": ["debug-test"], "env": {"PYTEST_ADDOPTS": "--no-cov"} } ] }
表示
では実際に表示してみましょう。 テストエクスプローラーのテストファイル横の実行アイコンをクリックします。これにより、テストファイル内の全てのテスト関数が実行されます。
テスト実行後、collatz.py
を開いた状態でステータスバーの「〇 Watch」をクリックします。
すると、以下のようにコード上にカバレッジが表示されます。 緑のラインはコードが実行されたことを、赤いラインはまだコードが実行されていないことを表しています。 また、先程クリックしたステータスバーの箇所にカバレッジが表示されます。ここでは 64% です。
上の画像から、collatz
関数に 0 以下の数を与えた時に例外が発生するケースがカバーされていないことが分かります。
最後にこのケースのテストを作成し、実行してみましょう。
以下のように import
文を修正し、test_collatz_not_positive_number_error
関数を test_collatz.py
に追加してください。
テストはこのファイルの関数全てに対して実行することに注意してください。
test_collatz.py
from collatz import collatz, is_even, NotPositiveNumberError def test_collatz_not_positive_number_error(): with pytest.raises(NotPositiveNumberError) as e: collatz(0)
実行すると先程の赤いラインが緑色になり、カバレッジが 68% に増えていることが確認できます。
おわりに
この記事では VS Code と pytest を使用した基本的なテスト方法とデバッグ、そしてカバレッジの表示方法を扱いました。特にカバレッジを表示すると、まだカバーされていない箇所が一目瞭然になるので、何に対するテストを書いたらいいかが分かりやすくなると思います。