pyてよn日記

一寸先は闇が人生

Python:pytest の基礎の基礎

ユニットテストの書き方,そもそもユニットテストって何なのって状態から以下の記事らを写経してみたのでメモ書き.Python のコードは一切出てこないです.

blog.amedama.jp

pytest の特徴

  • unittest と上位互換性がある.
  • unittest で書いたテストを pytest でそのまま実行できるため,unittest を使っているプロジェクトに導入しやすい.
  • デファクトになりつつある(らしい)

インストール

pytest とそのプラグインの含めた以下 3 つのパッケージをインストール.

  • pytest
  • pytest-flake8:Python のコーディング規約のチェック
  • pytest-cov:テストカバレッジの計測
$ pipenv install pytest pytest-flake8 pytest-cov

Python におけるテストの基礎

  • テストランナー:テストを実行する機能
  • テストディスカバリ:テストコードの命名によりテストランナーが自動でテストコードを探せる仕組み
  • テストファイルの命名には test_[foo].py のように "test" を必ず付ける

テストのディレクトリ構成

  • tests というテストコード専用のディレクトリを作り一箇所にまとめるのが一般的.
  • tests ディレクトリは,テスト対象のモジュール・パッケージと同じ階層に入れる.
  • 各パッケージ,tests ディレクトリに __init__.py を作成する.

例として,example というパッケージに対するテストを用意したプロジェクトの構成は以下のようになる.

$ tree
├── example
│   ├── __init__.py
│   ├── fizzbuzz.py
│   ├── foobar.py
│   └── helloworld.py
└── tests
    ├── __init__.py
    ├── test_fizzbuzz.py
    ├── test_foobar.py
    └── test_helloworld.py

テストの実行

  • 1 つのファイルを指定して実行

-v(verb)を入れると詳細が見れる.

$ pytest -v test_[hoge].py
  • 全てのテストコードを実行

先述したプラグイン--flake8--cov=[directory] というオプションで導入できる.カバレッジの測定に現在のディレクトリのみを指定する場合,--cov=.(ドットを使う)で指定できる.

$ pytest -v --flake8 --cov=.

出力例を以下に示す.実際に冒頭で紹介した記事の実行例.気になる方はそちらを参照してください.

$ pytest -v --flake8 --cov=.

============================================= test session starts =============================================
platform darwin -- Python 3.7.4, pytest-5.2.1, py-1.8.0, pluggy-0.13.0 -- /Users/pyteyon/Projects/test_project/.venv/bin/python3.7
cachedir: .pytest_cache
rootdir: /Users/pyteyon/Projects/test_project/
plugins: flake8-1.0.4, cov-2.8.1
collected 22 items                                                                                            

__init__.py::FLAKE8 SKIPPED                                  [  4%]
test_fizzbuzz.py::FLAKE8 SKIPPED                             [  9%]
test_fizzbuzz.py::test_fizzbuzz[1-1] PASSED                  [ 13%]
test_fizzbuzz.py::test_fizzbuzz[2-2] PASSED                  [ 18%]
test_fizzbuzz.py::test_fizzbuzz[3-Fizz] PASSED               [ 22%]
test_fizzbuzz.py::test_fizzbuzz[4-4] PASSED                  [ 27%]
test_fizzbuzz.py::test_fizzbuzz[5-Buzz] PASSED               [ 31%]
test_fizzbuzz.py::test_fizzbuzz[6-Fizz] PASSED               [ 36%]
test_fizzbuzz.py::test_fizzbuzz[7-7] PASSED                  [ 40%]
test_fizzbuzz.py::test_fizzbuzz[8-8] PASSED                  [ 45%]
test_fizzbuzz.py::test_fizzbuzz[9-Fizz] PASSED               [ 50%]
test_fizzbuzz.py::test_fizzbuzz[10-Buzz] PASSED              [ 54%]
test_fizzbuzz.py::test_fizzbuzz[11-11] PASSED                [ 59%]
test_fizzbuzz.py::test_fizzbuzz[12-Fizz] PASSED              [ 63%]
test_fizzbuzz.py::test_fizzbuzz[13-13] PASSED                [ 68%]
test_fizzbuzz.py::test_fizzbuzz[14-14] PASSED                [ 72%]
test_fizzbuzz.py::test_fizzbuzz[15-FizzBuzz] PASSED          [ 77%]
test_fizzbuzz.py::test_fizzbuzz[16-16] PASSED                [ 81%]
test_foobar.py::FLAKE8 SKIPPED                               [ 86%]
test_foobar.py::TestFoobar::test_do_something PASSED         [ 90%]
test_helloworld.py::FLAKE8 SKIPPED                           [ 95%]
test_helloworld.py::test_greet PASSED                        [100%]


---------- coverage: platform darwin, python 3.7.4-final-0 -----------
Name                 Stmts   Miss  Cover
----------------------------------------
__init__.py              0      0   100%
test_fizzbuzz.py         7      1    86%
test_foobar.py           8      0   100%
test_helloworld.py       7      1    86%
----------------------------------------
TOTAL                   22      2    91%


======================================== 18 passed, 4 skipped in 0.20s ========================================

ポイント

 記事内で特に重要そうだと思ったのが「テストしにくい部分をモックと入れ替える」という部分.テスト対象の動作に依存関係がある場合や処理に時間がかかる場合にそれらをどのようにテストするのか,というのは難しそう部分だなと思ったので覚書として言及しておいた.

後書き

 まだ使ったことすらないので有用性を実感できてないが,「ここまでは大丈夫」ということを保証できることで精神的に良さそう(テストが通った時の出力が気持ちいい).ソフトウェアの品質を高めるとかテスト自動化とかまだそんなレベルではないので勉強あるのみ.

参考記事

blog.amedama.jp