Python プロジェクトでユニットテストを実行する pytest を導入しカバレッジレポートを出力する

Python プロジェクトでユニットテストを行うため、pytest というツールを導入してみた。

目次

Python 用のユニットテストツール

Python には unittest という UT 用のモジュールが標準で搭載されていて、コレを使ってもユニットテストが簡単に行える。

しかし、複数のテストファイルの一括実行や、カバレッジレポートの出力が若干面倒臭そう (coverage モジュールを併用する) だったので、今回はテストランナーとして pytest を採用し、カバレッジレポート生成に pytest-cov を使用することにした。

ちなみに、Scrapy の Spider もテストしたいなぁーと思ったのだが、コチラは Scrapy が持っている Contracts という機能を使うと、簡単にテストできるらしいので、コレでいいかな。

pipenv でモジュールをインストールする

というワケで早速インストールしていこう。

$ pipenv install --dev pytest pytest-cov

コレだけ。

テストコードを書く

今回のプロジェクトは次のような構成とする。

example-project/
├ src/ … テストしたい既存コード
│ ├ __init__.py
│ └ my_example.py
└ tests/ … 今回新たに作るディレクトリ
   └ __init__.py
   └ test_my_example.py

テスト対象コードは ./src/ ディレクトリ配下にあるテイとし、./src/ と同じ階層に ./tests/ ディレクトリを作る。

./tests/ 配下には、空ファイルの __init__.py を作るのと、test_ から始まるテストコードを作る。pytest は tests ディレクトリや test_ から始まるファイルを自動的に特定して実行してくれるのだ。

空の ./tests/__init__.py というファイルがなぜ必要かというと、import におけるパス解決が上手く出来ず、以下のようなエラーが出るため。

ImportError: attempted relative import with no known parent package

./tests/__init__.py を用意することで、上手くモジュールと認識されるようになる。

テスト対象コードは次のようなモノをサンプルで用意した。

class MyExample():
  def for_unit_test():
    return 1

ただ 1 を返すだけの関数を持つクラスだ。

テストコードは次のような要領で書く。

# テスト対象クラスを import する
from src.my_example import MyExample

# 成功するテストケース
def test_for_unit_test_01():
  assert MyExample.for_unit_test() == 1

# わざと失敗するテストケースも用意した
def test_for_unit_test_02():
  assert MyExample.for_unit_test() == 2

実行してみる

とりあえず実行するなら次のように実行すれば良い。

$ pipenv shell
$ pytest

2件のテストケースが実行され、1件成功・1件失敗であることが分かるだろう。

ケース別にテスト状況を出力するなら、次のようにする。

$ pytest -v

pytest-cov を組み合わせて HTML 形式のカバレッジレポートを出力するには、次のように実行する。

$ pytest --cov=src --cov-report=html

--cov オプション引数でカバレッジレポートを取るディレクトリを指定し、--cov-report=html で HTML 形式で出力するよう設定している。

Pipfile で指定するなら次のようにすれば良い。

[scripts]
test = "pytest --cov=tickets --cov-report=html"

コレで pipenv run test と実行できるようになる。

HTML 形式のカバレッジレポートは ./htmlcov/index.html を開くと確認できる。見た目は Angular などに組み込まれている Istanbul Reporter に似ていて、分かりやすい。

テストを実行すると .coverage というファイルも生成されるので、以下2つは .gitignore で管理するようにしておこう。

# .gitignore
htmlcov/
.coverage

以上

pytest を使うとユニットテストの一括実行ができ、pytest-cov を組み合わせれば HTML 形式でのカバレッジレポートが簡単に出力できることが分かった。