LoginSignup
308
274

More than 3 years have passed since last update.

pytestのとりあえず知っておきたい使い方

Last updated at Posted at 2018-03-17

pytestとは

Pythonのテストツールです。
pipコマンドで簡単に導入できます。

pip install pytest

pytestの詳細、最新情報は以下サイト参考。
- docs.pytest.org
- The Hitchhiker's Guide to Python

オプションがいろいろあるので、とりあえず知っておくと便利なオプションを選んで
やりたいこと別にコマンドと表示例を記載します。

フォルダ構成、プログラム

として以下のフォルダ構成、プログラムについての実行結果を記載します。

  • フォルダ構成

    • mainフォルダに コード (テスト対象) を配置
    • testsフォルダに テストコード を配置
    • テストコードのファイル名は test_*.py とするのがお約束
study_pytest
 ├─main
 |  ├─ calc.py
 |  └─ say.py
 └─tests
    ├─ test_calc.py
    └─ test_say.py
  • コード
calc.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
say.py
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_(テスト対象のクラス名/定義関数名)_(検証観点)_(連番)
test_calc.py
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
test_say.py
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)
log.txt
. 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

308
274
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
308
274