GitHubにpush済みのpythonコードのテストカバレッジを計測してみる。

スポンサーリンク
Post image

DIY太陽光発電システム監視用のPythonドライバをCIしてみた記事の続編で、今回はpythonコードのカバレッジ(テストのカバー率)を計測します。

コマンドラインでカバレッジを計測する

まずはテストを実行

以下のようなtest runnerコードを毎回書くのは面倒なので、noseパッケージをpip installし、nosetestsコマンドを使います。

import unittest

if __name__ == "__main__":  
    all_tests = unittest.TestLoader().discover("./", "test_*.py")
    unittest.TextTestRunner(verbosity=1).run(all_tests)

nosetestsは、noseパッケージをpip installすると使えるようになるコマンドです。

nosetestsのテストケースの見つけ方を見ると分かりますが、とりあえずunittest.TestCaseのサブクラスをかき集めて実行してくれるようです。

tsmppt60_driver $ nosetests -v  
test_init (test_base_controller.TestChargeControllerStatus) ... ok  
test_compute_scaler_current (test_base_management.TestMb) ... ok  
test_compute_scaler_voltage (test_base_management.TestMb) ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.230s

OK  

setup.pyのtest_suiteに以下のように書いておき、noseにtest suiteをかき集めてもらって、setup.py経由でテスト実行することも可能です。

setup (  
    # ...
    test_suite='nose.collector'
)
$ python setup.py test

テストカバレッジ計測

カバレッジを計測するには、以下の--with-coverageオプションをつけます。

命令網羅だけでなく、--cover-branchesオプションで分岐網羅もできるようです。

tsmppt60_driver $ nosetests -h | grep coverage  
...
  --with-coverage       Enable plugin Coverage:  Activate a coverage report
  --cover-branches      Include branch coverage in coverage report
...

tsmppt60_driver $ nosetests -v --with-coverage  
test_init (test_base_controller.TestChargeControllerStatus) ... ok  
test_compute_scaler_current (test_base_management.TestMb) ... ok  
test_compute_scaler_voltage (test_base_management.TestMb) ... ok

Name                                                       Stmts   Miss  Cover   Missing  
----------------------------------------------------------------------------------------
minimock.py                                                  197     79    60%   51-57, 63-66, 108-109, 113, 229, 231-232, 237-238, 241-242, 258, 264-265, 290, 294, 297, 300, 325, 335-341, 364, 388-393, 408, 417-418, 455-465, 479-487, 493, 506, 517, 526, 528, 530-533, 537, 540-548, 551-565, 643-644  
...

カバレッジ計測結果は、同ディレクトリ下の.coverageファイルに記録されます。

tsmppt60_driver $ cat .coverage | head  
!coverage.py: This is a private format, don't read it directly!{"lines": {"/Users/

テストカバレッジレポート作成

coverageパッケージとコマンドを使って、カバレッジレポートを生成します。
pip installしてヘルプ表示すると、以下のようなサブコマンドが用意されていることが分かります。

$ pip install coverage
$ coverage --help
Coverage.py, version 4.0.3  
Measure, collect, and report on code coverage in Python programs.

usage: coverage <command> [options] [args]

Commands:  
    annotate    Annotate source files with execution information.
    combine     Combine a number of data files.
    erase       Erase previously collected coverage data.
    help        Get help on using coverage.py.
    html        Create an HTML report.
    report      Report coverage stats on modules.
    run         Run a Python program and measure code execution.
    xml         Create an XML report of coverage results.

カバレッジ計測結果が記録された.coverageファイルを読み込み、レポートを表示することができます。

--includeオプションで対象モジュールを絞らないと、テスト対象以外の依存モジュールも一緒くたにレポートしてしまい、煩雑になるのでご注意を。

tsmppt60_driver $ coverage report --include=tsmppt60_driver/*  
Name                          Stmts   Miss  Cover  
-------------------------------------------------
tsmppt60_driver/__init__.py      28     16    43%  
tsmppt60_driver/base.py         117     51    56%  
tsmppt60_driver/status.py        60     37    38%  
-------------------------------------------------
TOTAL                           205    104    49%  

コード内のどのパスがカバーできて、どのパスがカバーできていないのかを視覚的に確認できる、HTML形式のレポートも生成できます。

tsmppt60_driver $ coverage html --include=tsmppt60_driver/*  
tsmppt60_driver $ open htmlcov/index.html  

こんな感じです。

Webサービスを使って自動的にカバレッジを計測する

前置きが長くなりましたが、ここからが本題です。

前回紹介したTravisCIと連携しやすいCoverallsを使います。

.travis.ymlが設定済みであれば以下のような内容だったと思いますが、

language: python  
python:  
  - "2.7"
# - "3.2"
# TODO:
# dest.write(u'  ')
# ^
# SyntaxError: invalid syntax
  - "3.3"
  - "3.4"
  - "3.5"
  # does not have headers provided, please ask https://launchpad.net/~pypy/+archive/ppa
  # maintainers to fix their pypy-dev package.
  - "pypy"
# command to install dependencies
install:  
  - pip install .
  - pip install -r requirements.txt -r test-requirements.txt

これに以下を追記して、

script:  
  - coverage run --source=tsmppt60_driver setup.py test
after_success:  
  - coveralls

test-requirements.txtには以下を追記します。

nose  
coverage  
coveralls  

ほとんどcoveralls-pythonのREADMEに従うだけなんですけどね。

これでgithubリポジトリにpushする度、自動的にTravisCIでテストを回し、自動的にCoverallsでカバレッジ計測してくれます。

バッジの表示

Coverallsのカバレッジ計測結果バッジ

coverallsのREPOSで、カバレッジ計測後に対象のリポジトリのリンクに飛び、画面上の方に「BADGE YOUR REPO: TSMPPT60_DRIVER」というリンクがあるので、そこでバッジのURLを取得できます。

MARKDOWN形式他、一通り揃っているので、環境に合わせてコピペして使ってください。

GitHubのREADMEにも、以下のようなバッジを貼ることができます。

Coverage Status

いろいろなバッジ

TravisCIやCoverallsのようなバッジをREADMEに貼ると、なんだか他の情報も同じようなバッジで表示したくなります。よね?

そこで、shields.ioというサービスがあります。

上記リンク先の通り、以下のようなフォーマットで様々なバッジを自分で作ることができます。

https://img.shields.io/badge/<SUBJECT>-<STATUS>-<COLOR>.svg  

例えば以下のようにすると、

https://img.shields.io/badge/python-3.3,3.4,3.5-blue.svg  

以下のように表示されます。

python

あまり意味はないですが、こんなバッジも作れちゃいます。

hoge hoge hoge

comments powered by Disqus