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

[DIY太陽光発電システム監視用のPythonドライバをCIしてみた記事](http://blog.rinka-blossom.com/githubnipushji-minopythonkodowocisitemita/)の続編で、今回はpythonコードのカバレッジ（テストのカバー率）を計測します。

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

## まずはテストを実行

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

```python
import unittest

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

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

[nosetestsのテストケースの見つけ方](http://nose.readthedocs.org/en/latest/finding_tests.html)を見ると分かりますが、とりあえずunittest.TestCaseのサブクラスをかき集めて実行してくれるようです。

```bash
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に以下のように書いておき](http://nose.readthedocs.org/en/latest/setuptools_integration.html)、noseにtest suiteをかき集めてもらって、setup.py経由でテスト実行することも可能です。

```python
setup (
    # ...
    test_suite='nose.collector'
)
```

```bash
$ python setup.py test
```

## テストカバレッジ計測

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

```bash
tsmppt60_driver $ nosetests -h | grep coverage
  --with-coverage       Enable plugin Coverage:  Activate a 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ファイルに記録されます。

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

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

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

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

```bash
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形式のレポートも生成できます。

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

こんな感じです。

![](https://farm2.staticflickr.com/1515/24413123892_ab1d5b8613_z_d.jpg)

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

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

[前回紹介したTravisCI](http://blog.rinka-blossom.com/githubnipushji-minopythonkodowocisitemita/)と連携しやすい[Coveralls](https://coveralls.io/)を使います。

.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](https://github.com/coagulant/coveralls-python)のREADMEに従うだけなんですけどね。

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

![](https://farm2.staticflickr.com/1638/23537481564_56c23632fe_z_d.jpg)

# バッジの表示

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

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

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

![](https://farm2.staticflickr.com/1672/23797864439_021774c782_z_d.jpg)

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

[![Coverage Status](https://coveralls.io/repos/dodo5522/tsmppt60_driver/badge.svg?branch=master&service=github)](https://coveralls.io/github/dodo5522/tsmppt60_driver?branch=master)

## いろいろなバッジ

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

そこで、[shields.io](http://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](https://img.shields.io/badge/python-3.3,3.4,3.5-blue.svg)](https://pypi.python.org/pypi/tsmppt60_driver)

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

[![hoge](https://img.shields.io/badge/hello-world-red.svg)]()
[![hoge](https://img.shields.io/badge/mac-book-green.svg)]()
[![hoge](https://img.shields.io/badge/ubuntu-14.04-yellow.svg)]()