ユニットテストの書き方,そもそもユニットテストって何なのって状態から以下の記事らを写経してみたのでメモ書き.Python のコードは一切出てこないです.
pytest の特徴
- unittest と上位互換性がある.
- unittest で書いたテストを pytest でそのまま実行できるため,unittest を使っているプロジェクトに導入しやすい.
- デファクトになりつつある(らしい)
インストール
pytest とそのプラグインの含めた以下 3 つのパッケージをインストール.
$ 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 ========================================
ポイント
記事内で特に重要そうだと思ったのが「テストしにくい部分をモックと入れ替える」という部分.テスト対象の動作に依存関係がある場合や処理に時間がかかる場合にそれらをどのようにテストするのか,というのは難しそう部分だなと思ったので覚書として言及しておいた.
後書き
まだ使ったことすらないので有用性を実感できてないが,「ここまでは大丈夫」ということを保証できることで精神的に良さそう(テストが通った時の出力が気持ちいい).ソフトウェアの品質を高めるとかテスト自動化とかまだそんなレベルではないので勉強あるのみ.