こんにちは!BFT名古屋支店の猫です。
先日初めてpytestを使用してみたので、学んだことを3記事に分けてまとめました。
今回は 第一弾 導入~カバレッジ測定編 として、pytestを導入してカバレッジを測定するまでの手順をご紹介します。
pytestをこれから使う/最近使い始めた方の参考になればと思います。
pytestとは
pytestとは、Pythonコードをテストするためのツールです。
実行する関数や引数、返り値を設定して、想定通りにプログラムが動作するかを確認することができます。
docs.pytest.org
環境
- Ubuntu 18.04.5 LTS (WSL1)
- Python 3.8.10
- pytest実行やカバレッジ測定に必要なライブラリ
- pytest 6.2.5
- coverage 5.5
- pytest-cov 2.12.1
- pytest-mock 3.6.1
ディレクトリ構成
project_root_dir … プロジェクトルートディレクトリ ├── lib │ ├── ErrorClass.py … Arithmetic.pyに呼ばれるファイル │ └── __init__.py ├── code_dir │ ├── __init__.py │ └── Arithmetic.py … テスト対象のファイル ├── testcode_dir │ └── test_Arithmetic.py … テストコードが書かれたファイル └── pytest.ini … pytest実行に必要なファイル
テスト対象コード
例として、引数の2整数に対して四則演算した結果を返すプログラムを作りました。
●Arithmetic.py
import json import os from datetime import datetime as dt from lib import ErrorClass as ec ### 現在日時を取得する関数 def get_date(): return dt.now().strftime('%Y-%m-%d %H:%M:%S') ### 引数で指定された演算を行う関数 def calc(var1:int, var2:int, method:str): if method == 'add': return var1 + var2 elif method == 'sub': return var1 - var2 elif method == 'mult': return var1 * var2 elif method == 'div': return var1 / var2 ### ### メイン関数:引数で指定された2整数の四則演算の結果を出力する ### def main(var1:int, var2:int): result = dict() result['date'] = get_date() try: if type(var1) != int or type(var2) != int: raise ec.TypeError result['sum'] = calc(var1, var2, 'add') result['diff'] = calc(var1, var2, 'sub') result['prod'] = calc(var1, var2, 'mult') result['quot'] = calc(var1, var2, 'div') except ec.TypeError as e: result['error_code'] = e.__class__.__name__ except: result['error_code'] = 'MyException' # 結果書き込み処理 if not os.path.isfile('result.json'): with open('result.json', 'w') as fp: json.dump(result,fp) return result
●ErrorClass.py
# エラークラスの定義 class TypeError(Exception): def error_code(self): pass
pytest導入
①ライブラリのインストール
pipコマンドで、pytest実行とカバレッジ測定に必要なライブラリをインストールします。
$ pip install pytest coverage pytest-cov
②PYTHONPATHを通す
色々な方法があるかと思いますが、今回はホームディレクトリ配下の.bashrcに以下を追加しました。
pytestを実行するにはいくつかのディレクトリにパスを通す必要があったため、コロン(:)を使用して複数のディレクトリを指定しました。
export PYTHONPATH=/mnt/c/Users/****/project_root_dir:/mnt/c/Users/****/project_root_dir/code_dir
Windows版を利用される方は、ユーザー環境変数 "PYTHONPATH" を新規作成 > 値にフォルダのパスを登録して、再起動(大事!)してください。
③pytest.iniの作成
pytest.iniという名前のファイルを作成し、プロジェクトのルートディレクトリ直下に配置します。
pytest.iniでは、実行するテストコードを指定することができます。
使用しなくても実行はできますが、テストコードが増えた時に部分的に実行するのに便利です。
[pytest] testpaths = /mnt/c/Users/****/project_root_dir/testcode_dir … テストコードのパスを指定 python_files = test_Arithmetic.py … テストコードのファイル名を指定 python_functions = test_ … 「test_」で始まる関数名のテストを実行する
④__init__.pyの作成と配置
__init__.pyという名前の空ファイルを作成し、テスト対象のコードが配置されているディレクトリそれぞれに配置します。
今回の例ですと、Arithmetic.pyがあるcode_dirディレクトリだけでなく、ErrorClass.pyがあるlibディレクトリにも配置します。
⑤テストコードの作成
正常系と異常系で一つずつテストコードを用意しました。
import Arithmetic # 正常系 def test_success(): res = Arithmetic.main(1,2) assert res['sum'] == 3 # 想定される結果を「assert <式>」で書く assert res['diff'] == -1 # 異常系 def test_TypeError(): res = Arithmetic.main(1.5,1) # 整数でない引数を渡しているため例外が起こる assert res['error_code'] == "TypeError"
⑥pytest実行&結果確認
pytestコマンドでテストを実行します。
以下の例では詳細な結果を表示するための-v
オプションと、カバレッジを測定するための--cov
オプションを使用しています。
測定したカバレッジは$ coverage html <カバレッジ測定対象>
を実行してhtml形式で保存することができます。
$ pytest -v --cov=Arithmetic ========================= test session starts ========================= platform linux -- Python 3.8.10, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 -- /home/****/.pyenv/versions/3.8.10/envs/prac_venv/bin/python3.8 cachedir: .pytest_cache rootdir: /mnt/c/Users/****/project_root_dir, configfile: pytest.ini, testpaths: /mnt/c/Users/****/project_root_dir/testcode_dir plugins: cov-3.0.0, mock-3.6.1 collected 2 items testcode_dir/test_Arithmetic.py::test_success PASSED [ 50%] testcode_dir/test_Arithmetic.py::test_TypeError PASSED [100%] ---------- coverage: platform linux, python 3.8.10-final-0 ----------- Name Stmts Miss Cover -------------------------------------- code_dir/Arithmetic.py 33 4 88% -------------------------------------- TOTAL 33 4 88% ========================== 2 passed in 0.12s ========================== $ coverage html Arithmetic.py
上記のコマンドを実行後 <プロジェクトルートディレクトリ>/htmlcov/Arithmetic_py.htmlを開くと、以下の画面が表示されます。
白くなっているのはテストにより実行された部分、赤くなっているのは実行されていない部分を表します。
この例だと、42~43行目と47~48行目が赤くなっているので実行されていないことが分かります。
おわりに
今回測定したカバレッジは 88% でした!
次回はカバレッジを100%に近づけるために実施した方法を4つご紹介します。
第二弾 カバレッジを上げるためにやったこと編 お楽しみに!