#pytestとは
Pythonのテストツールです。
pipコマンドで簡単に導入できます。
pip install pytest
pytestの詳細、最新情報は以下サイト参考。
オプションがいろいろあるので、とりあえず知っておくと便利なオプションを選んで
やりたいこと別にコマンドと表示例を記載します。
#フォルダ構成、プログラム
例として以下のフォルダ構成、プログラムについての実行結果を記載します。
-
フォルダ構成
-
mainフォルダに コード (テスト対象) を配置
-
testsフォルダに テストコード を配置
-
テストコードのファイル名は test_*.py とするのがお約束
study_pytest
├─main
| ├─ calc.py
| └─ say.py
└─tests
├─ test_calc.py
└─ test_say.py
- コード
class Calc:
def __init__(self, a, b):
self.a = a
self.b = b
def add(self):
return self.a + self.b
def dif(self):
return self.a - self.b
def seki(self):
return self.a*self.b
def shou(self):
return self.a/self.b
class Foo:
def say(self):
return 'foo'
def say2(self):
return 'foo2'
class Hoge:
def say(self):
return 'hoge'
def say2(self):
return 'hoge2'
-
テストコード
-
ダブルメンテにならないように、テスト対象はcallして実行する
-
必要により、システム環境変数PYTHONPATHにパスを追加する
-
テストコードの各ケースは独立であること
-
テストコードの各ケースの名前(定義関数名)は説明的であること
- テスト結果に表示されたときに、名前からわかるようにします。
- 例: test_(テスト対象のクラス名/定義関数名)_(検証観点)_(連番)
from main.calc import Calc
def test_add_01():
assert Calc(9,2).add() == 11
def test_add_02():
assert Calc(-9,2).add() == -7
def test_dif_01():
assert Calc(9,2).dif() == 7
def test_dif_02():
assert Calc(-9,2).dif() == -11
def test_seki_01():
assert Calc(9,2).seki() == 18
def test_seki_02():
assert Calc(-9,2).seki() == -18
def test_shou_01():
assert Calc(9,2).shou() == 4.5
def test_shou_02():
assert Calc(-9,2).shou() == -4.5
from main.say import Foo,Hoge
def test_foo_say():
assert Foo().say() == 'foo'
def test_foo_say2():
assert Foo().say2() == 'foo' #テストエラーの出力確認。
def test_hoge_say():
assert Hoge().say() == 'hoge'
def test_hoge_say2():
assert Hoge().say2()== 'hoge2'
#テスト実行コマンド(基本)
study_pytest または study_pytest/testsのディレクトリ に移動してコマンドを実施します。
すべてのテストコードを実施
pytest
指定テストコードのみ実施
- テストコードファイルを引数に指定する
pytest tests/test_calc.py
##ヘルプの表示、オプションを確認したい
pytest -h
#テスト実行コマンド(応用)
やりたいこと別に コマンド、出力結果 を記載します。
オプションなし
- コマンド
pytest
- 実行結果
study_pytest>pytest
============================= test session starts =============================
platform win32 -- Python 3.6.3, pytest-3.4.2, py-1.5.2, pluggy-0.6.0
rootdir: C:\Users\xxx\python\study_pytest, inifile:
collected 12 items
tests\test_calc.py ........ [ 66%]
tests\test_say.py .F.. [100%]
================================== FAILURES ===================================
________________________________ test_foo_say2 ________________________________
def test_foo_say2():
> assert Foo().say2() == 'foo'
E AssertionError: assert 'foo2' == 'foo'
E - foo2
E ? -
E + foo
tests\test_say.py:7: AssertionError
===================== 1 failed, 11 passed in 0.13 seconds =====================
##テストケース別に結果を知りたい
テストケースごとに PASSED/FAILED が表示される。
- コマンド
pytest -v
- 実行結果
study_pytest>pytest -v
============================= test session starts =============================
platform win32 -- Python 3.6.3, pytest-3.4.2, py-1.5.2, pluggy-0.6.0 -- c:\users\xxx\appdata\local\programs\python\python36\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\xxx\Documents\python\study_pytest, inifile:
collected 12 items
tests/test_calc.py::test_add_01 PASSED [ 8%]
tests/test_calc.py::test_add_02 PASSED [ 16%]
tests/test_calc.py::test_dif_01 PASSED [ 25%]
tests/test_calc.py::test_dif_02 PASSED [ 33%]
tests/test_calc.py::test_seki_01 PASSED [ 41%]
tests/test_calc.py::test_seki_02 PASSED [ 50%]
tests/test_calc.py::test_shou_01 PASSED [ 58%]
tests/test_calc.py::test_shou_02 PASSED [ 66%]
tests/test_say.py::test_foo_say PASSED [ 75%]
tests/test_say.py::test_foo_say2 FAILED [ 83%]
tests/test_say.py::test_hoge_say PASSED [ 91%]
tests/test_say.py::test_hoge_say2 PASSED [100%]
================================== FAILURES ===================================
________________________________ test_foo_say2 ________________________________
def test_foo_say2():
> assert Foo().say2() == 'foo'
E AssertionError: assert 'foo2' == 'foo'
E - foo2
E ? -
E + foo
tests\test_say.py:7: AssertionError
===================== 1 failed, 11 passed in 0.18 seconds =====================
前回NGだったケースだけテストしたい
last-failed (前回failedだったケース) のみ実施する。
- コマンド
pytest -v --lf
- 実行結果
study_pytest>pytest -v --lf
============================= test session starts =============================
platform win32 -- Python 3.6.3, pytest-3.4.2, py-1.5.2, pluggy-0.6.0 -- c:\users\xxx\appdata\local\programs\python\python36\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\xxx\Documents\python\study_pytest, inifile:
collected 12 items
run-last-failure: rerun previous 1 failure
tests/test_say.py::test_foo_say2 FAILED [100%]
================================== FAILURES ===================================
________________________________ test_foo_say2 ________________________________
def test_foo_say2():
> assert Foo().say2() == 'foo'
E AssertionError: assert 'foo2' == 'foo'
E - foo2
E ? -
E + foo
tests\test_say.py:7: AssertionError
============================= 11 tests deselected =============================
=================== 1 failed, 11 deselected in 0.12 seconds ===================
前回NGだったケースからテストしたい
failed-first (前回failedだったケースから) で実施する。
前回正常終了分も続けて実施される。
- コマンド
pytest -v --ff
- 実行結果
study_pytest>pytest -v --ff
============================= test session starts =============================
platform win32 -- Python 3.6.3, pytest-3.4.2, py-1.5.2, pluggy-0.6.0 -- c:\users\xxx\appdata\local\programs\python\python36\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\xxx\Documents\python\study_pytest, inifile:
collected 12 items
run-last-failure: rerun previous 1 failure first
tests/test_say.py::test_foo_say2 FAILED [ 8%]
tests/test_calc.py::test_add_01 PASSED [ 16%]
tests/test_calc.py::test_add_02 PASSED [ 25%]
tests/test_calc.py::test_dif_01 PASSED [ 33%]
tests/test_calc.py::test_dif_02 PASSED [ 41%]
tests/test_calc.py::test_seki_01 PASSED [ 50%]
tests/test_calc.py::test_seki_02 PASSED [ 58%]
tests/test_calc.py::test_shou_01 PASSED [ 66%]
tests/test_calc.py::test_shou_02 PASSED [ 75%]
tests/test_say.py::test_foo_say PASSED [ 83%]
tests/test_say.py::test_hoge_say PASSED [ 91%]
tests/test_say.py::test_hoge_say2 PASSED [100%]
================================== FAILURES ===================================
________________________________ test_foo_say2 ________________________________
def test_foo_say2():
> assert Foo().say2() == 'foo'
E AssertionError: assert 'foo2' == 'foo'
E - foo2
E ? -
E + foo
tests\test_say.py:7: AssertionError
===================== 1 failed, 11 passed in 0.21 seconds =====================
テスト時間を記録したい
テストコードの定義関数ごとの時間を表示する。
処理時間の結果について、1つの定義関数につき setup/call/teardown に分かれることに注意。
- コマンド
pytest -v --duration=0
- 実行結果
study_pytest>pytest -v --duration=0
============================= test session starts =============================
platform win32 -- Python 3.6.3, pytest-3.4.2, py-1.5.2, pluggy-0.6.0 -- c:\users\xxx\appdata\local\programs\python\python36\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\xxx\Documents\python\study_pytest, inifile:
collected 12 items
tests/test_calc.py::test_add_01 PASSED [ 8%]
tests/test_calc.py::test_add_02 PASSED [ 16%]
tests/test_calc.py::test_dif_01 PASSED [ 25%]
tests/test_calc.py::test_dif_02 PASSED [ 33%]
tests/test_calc.py::test_seki_01 PASSED [ 41%]
tests/test_calc.py::test_seki_02 PASSED [ 50%]
tests/test_calc.py::test_shou_01 PASSED [ 58%]
tests/test_calc.py::test_shou_02 PASSED [ 66%]
tests/test_say.py::test_foo_say PASSED [ 75%]
tests/test_say.py::test_foo_say2 FAILED [ 83%]
tests/test_say.py::test_hoge_say PASSED [ 91%]
tests/test_say.py::test_hoge_say2 PASSED [100%]
=========================== slowest test durations ============================
0.00s call tests/test_say.py::test_foo_say2
0.00s setup tests/test_calc.py::test_add_01
0.00s teardown tests/test_say.py::test_foo_say
0.00s call tests/test_say.py::test_hoge_say2
0.00s teardown tests/test_say.py::test_foo_say2
0.00s call tests/test_calc.py::test_add_01
0.00s setup tests/test_calc.py::test_add_02
0.00s call tests/test_calc.py::test_seki_01
0.00s teardown tests/test_calc.py::test_shou_01
0.00s teardown tests/test_calc.py::test_dif_02
0.00s setup tests/test_calc.py::test_dif_02
0.00s call tests/test_say.py::test_foo_say
0.00s teardown tests/test_calc.py::test_shou_02
0.00s call tests/test_say.py::test_hoge_say
0.00s call tests/test_calc.py::test_shou_02
0.00s call tests/test_calc.py::test_shou_01
0.00s call tests/test_calc.py::test_seki_02
0.00s call tests/test_calc.py::test_dif_02
0.00s call tests/test_calc.py::test_dif_01
0.00s call tests/test_calc.py::test_add_02
0.00s teardown tests/test_say.py::test_hoge_say2
0.00s setup tests/test_say.py::test_hoge_say2
0.00s teardown tests/test_say.py::test_hoge_say
0.00s setup tests/test_say.py::test_hoge_say
0.00s setup tests/test_say.py::test_foo_say2
0.00s setup tests/test_say.py::test_foo_say
0.00s setup tests/test_calc.py::test_shou_02
0.00s setup tests/test_calc.py::test_shou_01
0.00s teardown tests/test_calc.py::test_seki_02
0.00s setup tests/test_calc.py::test_seki_02
0.00s teardown tests/test_calc.py::test_seki_01
0.00s setup tests/test_calc.py::test_seki_01
0.00s teardown tests/test_calc.py::test_dif_01
0.00s setup tests/test_calc.py::test_dif_01
0.00s teardown tests/test_calc.py::test_add_02
0.00s teardown tests/test_calc.py::test_add_01
================================== FAILURES ===================================
________________________________ test_foo_say2 ________________________________
def test_foo_say2():
> assert Foo().say2() == 'foo'
E AssertionError: assert 'foo2' == 'foo'
E - foo2
E ? -
E + foo
tests\test_say.py:7: AssertionError
===================== 1 failed, 11 passed in 0.26 seconds =====================
遅いテストケースを見つけたい
遅かったテストTOP N個の時間を並べて表示する。
- コマンド
pytest -v --duration=N
- 実行結果
study_pytest>pytest -v --duration=3
============================= test session starts =============================
platform win32 -- Python 3.6.3, pytest-3.4.2, py-1.5.2, pluggy-0.6.0 -- c:\user\xxx\appdata\local\programs\python\python36\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\xxx\Documents\python\study_pytest, inifile:
collected 12 items
tests/test_calc.py::test_add_01 PASSED [ 8%]
tests/test_calc.py::test_add_02 PASSED [ 16%]
tests/test_calc.py::test_dif_01 PASSED [ 25%]
tests/test_calc.py::test_dif_02 PASSED [ 33%]
tests/test_calc.py::test_seki_01 PASSED [ 41%]
tests/test_calc.py::test_seki_02 PASSED [ 50%]
tests/test_calc.py::test_shou_01 PASSED [ 58%]
tests/test_calc.py::test_shou_02 PASSED [ 66%]
tests/test_say.py::test_foo_say PASSED [ 75%]
tests/test_say.py::test_foo_say2 FAILED [ 83%]
tests/test_say.py::test_hoge_say PASSED [ 91%]
tests/test_say.py::test_hoge_say2 PASSED [100%]
========================== slowest 3 test durations ===========================
0.00s call tests/test_say.py::test_foo_say2
0.00s setup tests/test_calc.py::test_add_01
0.00s call tests/test_calc.py::test_dif_02
================================== FAILURES ===================================
________________________________ test_foo_say2 ________________________________
def test_foo_say2():
> assert Foo().say2() == 'foo'
E AssertionError: assert 'foo2' == 'foo'
E - foo2
E ? -
E + foo
tests\test_say.py:7: AssertionError
===================== 1 failed, 11 passed in 0.20 seconds =====================
テストログをテキストファイルに出力したい
この機能は 次のVUP時(V4.0)にて閉塞される予定。
出力ログは、コマンドの実行ログより情報量が少ない。
- コマンド
pytest -v --result-log=tests/log.txt
- 実行結果(log)
. tests/test_calc.py::test_add_01
. tests/test_calc.py::test_add_02
. tests/test_calc.py::test_dif_01
. tests/test_calc.py::test_dif_02
. tests/test_calc.py::test_seki_01
. tests/test_calc.py::test_seki_02
. tests/test_calc.py::test_shou_01
. tests/test_calc.py::test_shou_02
. tests/test_say.py::test_foo_say
F tests/test_say.py::test_foo_say2
def test_foo_say2():
> assert Foo().say2() == 'foo'
E AssertionError: assert 'foo2' == 'foo'
E - foo2
E ? -
E + foo
tests\test_say.py:7: AssertionError
. tests/test_say.py::test_hoge_say
. tests/test_say.py::test_hoge_say2
テストがpassしたときもprint出力を表示したい
「passed」 のときにもprint出力をコンソール上に表示させる。
オプション指定しない場合、「failed」 のときしか表示されない。
- コマンド
pytest -v --capture=no
カバレッジを計測したい
カバレッジの計測も難しくないが、追加のpluginの導入が必要。
導入方法から以下のページに記載。
https://qiita.com/kg1/items/e2fc65e4189faf50bfe6