Travis CI + Codecov + NoseでCIしてSlackに通知
きっかけ
これまでもTravis CIを使ってきていて、それ自体にはそんなに戸惑うことは無かったんですが、カバレッジも取得したくなったりしたので少し調べたら日本語情報があまりなかったのでここに残しておくことにしました。
環境
開発環境は以下
必要なPythonモジュールなど
- nose 1.3.7
- codecov 2.0.15
- coverage 4.5.1
各種サービスのセットアップ
Travis CI
Circle CIがどうにも複雑に見えてしまいずっとTravisを使っているため、今回もTravisです。
特に難しいところはなく、普通にサインアップしてCIしたいリポジトリを有効にするだけです。Build only if .travis.yml is present にチェックを入れておくと良いと思います。
また、シークレットなトークンを暗号化するのに使うtravisというコマンドをgemでインストールします。
$ gem install travis $ travis login
.travis.ymlの文法エラーでCIをパスできないとめちゃくちゃ悲しくなるのでlintコマンドを覚えると良いと思います。
$ cat .travis.yml language: python python: - "3.5" - "3.6" script: - nosetests -v $ travis lint Hooray, .travis.yml looks valid :)
Codecov
Codecovはカバレッジを表示してくれるWebサービスです。Pythonにはcodecovというpipでインストールできるモジュールがあります*1。
同様のサービスとしてCoverallsなどがありますが、あまりデザインが好みで無かった上、Python用のパッケージが2種類あってうーんという感じだったのでこちらにしました*2。
上記リンクから、Githubアカウント、Gitlabアカウント、Bitbucketアカウントでサインアップできます。それぞれのオンプレ版にも対応しているみたいなので、かなり良さそうです。また、Teamでの使用もパブリックリポジトリなら無制限?のようです。プライベートリポジトリでの利用を検討している場合はリポジトリごと、またはユーザ数単位での課金になるようです*3。

有効にするリポジトリを選択するとトークンが表示され、カバレッジリポートのアップロードをしろみたいな画面になりますが、Travis CIから使うなら無視して良いです。

設定はもう終わりです(すごい)。
リポジトリのセットアップ
テストする
僕はずっとnoseを使っているので、今回もこれで行きます。フォルダ構成は以下の様にしました。student_portal_crawlerが今回テストするモジュールです。
$ tree -L 3 -I venv
.
├── LICENSE
├── README.md
├── requirements.txt
└── student_portal_crawler
├── __init__.py
├── browser.py
├── page
├── parser
└── test
├── __init__.py
└── test_script.py
noseのインストールおよびテスト実行は下記の様にします。テストの書き方についてはここでは解説しません。
$ pip install nose # テストの実行 $ nosetests ............................. ---------------------------------------------------------------------- Ran 20 tests in 0.680s OK # テストの実行とカバレッジの計算 $ pip install coverage $ nosetests --with-coverage
このままでは使用しているモジュール全てのカバレッジを取得してしまうので、--cover-packageオプションで取得するモジュールを指定してやります。今回の場合はstudent_portal_crawlerになります。
$ nosetests --with-coverage --cover-package=student_portal_crawler ............................. Name Stmts Miss Branch BrPart Cover --------------------------------------------------------------------------------------- student_portal_crawler/__init__.py 1 0 0 0 100% student_portal_crawler/browser.py 20 0 2 0 100% student_portal_crawler/page/__init__.py 1 0 0 0 100% student_portal_crawler/page/base.py 34 4 6 1 78% student_portal_crawler/parser/__init__.py 4 0 0 0 100% student_portal_crawler/parser/base.py 38 0 10 0 100% student_portal_crawler/parser/lec_info.py 18 0 6 0 100% student_portal_crawler/parser/static.py 5 0 0 0 100% student_portal_crawler/parser/utils.py 9 0 2 0 100% student_portal_crawler/shibboleth_login/__init__.py 1 0 0 0 100% student_portal_crawler/shibboleth_login/login.py 53 2 8 0 97% --------------------------------------------------------------------------------------- TOTAL 184 6 34 1 95% ---------------------------------------------------------------------- Ran 29 tests in 0.319s OK
.travis.ymlを書く
シンプルにPythonのバージョンいくつかでテストし、パスしたらCodecovにカバレッジを送信、Slackに結果を通知します。
まずは自分のSlackのIntegrationからTravis CIを追加します。Hubotとかで節約している人も居るかとは思いますが、僕は面倒なので豪華に行きます。

追加したら投稿するチャンネルを決め、トークンを取得します。

Travis CIでの使い方についての説明の中に、トークンの暗号化についての項目があるのでコピペして実行します。事前に.travis.ymlがないとエラーが出るので作成しておきます。

$ touch .travis.yml
$ travis encrypt "{{チーム名}}:{{トークン}}" --add notifications.slack
$ cat .travis.yml
notifications:
slack:
secure: npdNlesznnxrC7KsuB7....
Travis CIで利用可能なPythonバージョンは複数有り、最新は3.8-devやnightlyまで使える*4ようなのですが、
Recent Python development branches require OpenSSL 1.0.2+. As this library is not available for Trusty, 3.7-dev, 3.8-dev, and nightly do not work (or use outdated archive).
という記述があり、どうやったら使えるのか分からず3.6-devまでしか使っていません…*5
最終的に下記の様にしました。
language: python python: - "3.5" - "3.5-dev" - "3.6" - "3.6-dev" install: - pip install -r requirements.txt - pip install codecov coverage script: - nosetests -v --with-coverage --cover-package=student_portal_crawler after_success: - codecov notifications: email: false slack: secure: npdNlesznnxrC7K...
after_successでcodecovコマンドを実行することで、Codecovにカバレッジリポートが送信されます。トークンなどを加える必要は有りません。
また、Slackへの通知のみで十分なのでメール通知はオフにしています。
.coveragercを書く
このままではテストコードまで含めたカバレッジがcodecovに表示されてしまったり、テストしないコード(デバッグ用のステートメントなど)まで含まれてしまうため、.coveragercという設定ファイルを置いて制御します。
[run]
branch = True
source = student_portal_crawler
[report]
exclude_lines =
if TYPE_CHECKING:
if __name__ == .__main__.:
ignore_errors = True
omit =
student_portal_crawler/test/*
typingモジュールを使用して型ヒントの定義をしているので、常にFalseとなり実行されないif TYPE_CHECKING:という文が所々に現れるのでこれを排除しています。
また、実際の実行コード(if __name__ == '__main':)も排除しています。
READMEへバッジを貼る
CI回してカバレッジ取得する最大の目的といっても過言ではない、READMEへのバッジを貼ります。
Travis CI
https://travis-ci.org/ユーザ名/リポジトリ名にアクセスし、リポジトリ名の横に表示されているバッジをクリックすると、形式を選択してコピーできます。

Codecov
https://codecov.io/gh/ユーザ名/リポジトリ名/settings/badgeにアクセスすると形式を選択してコピーできます

後はpushすればCIが走ります。
結果を確認
Githubへpushしてしばらく待つとテストが完了します。

こんな風にSlackに通知が来ます。

Codecovに自動でカバレッジが送信され、以下の様に表示されます。

また、リンクを開いていくとどのモジュールのカバレッジが低いのか丸わかりな上、各ファイルのどの行がテストされていないのかも一発で分かります。

まとめ
取り扱いが面倒なことが多いトークン等の設定が必要ないため、Codecov + Travis CIはなかなかオススメの組み合わせかと思います。
どんどんunittestを書いて徳を積んでいきましょう!