ぽよメモ

レガシーシステム考古学専攻

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.io

Codecovはカバレッジを表示してくれるWebサービスです。Pythonにはcodecovというpipでインストールできるモジュールがあります*1

同様のサービスとしてCoverallsなどがありますが、あまりデザインが好みで無かった上、Python用のパッケージが2種類あってうーんという感じだったのでこちらにしました*2

上記リンクから、Githubアカウント、Gitlabアカウント、Bitbucketアカウントでサインアップできます。それぞれのオンプレ版にも対応しているみたいなので、かなり良さそうです。また、Teamでの使用もパブリックリポジトリなら無制限?のようです。プライベートリポジトリでの利用を検討している場合はリポジトリごと、またはユーザ数単位での課金になるようです*3

f:id:pudding_info:20180311181631p:plain

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

f:id:pudding_info:20180311181256p:plain

設定はもう終わりです(すごい)。

リポジトリのセットアップ

テストする

僕はずっと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とかで節約している人も居るかとは思いますが、僕は面倒なので豪華に行きます。

f:id:pudding_info:20180311184429p:plain

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

f:id:pudding_info:20180311184634p:plain

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

f:id:pudding_info:20180311184825p:plain

$ touch .travis.yml
$ travis encrypt "{{チーム名}}:{{トークン}}" --add notifications.slack
$ cat .travis.yml
notifications:
  slack:
    secure: npdNlesznnxrC7KsuB7....

Travis CIで利用可能なPythonバージョンは複数有り、最新は3.8-devnightlyまで使える*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_successcodecovコマンドを実行することで、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/ユーザ名/リポジトリ名にアクセスし、リポジトリ名の横に表示されているバッジをクリックすると、形式を選択してコピーできます。

f:id:pudding_info:20180311200415p:plain

Codecov

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

f:id:pudding_info:20180311200633p:plain

後はpushすればCIが走ります。

結果を確認

Githubへpushしてしばらく待つとテストが完了します。

f:id:pudding_info:20180311203617p:plain

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

f:id:pudding_info:20180311203517p:plain

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

f:id:pudding_info:20180311204144p:plain

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

f:id:pudding_info:20180311204324p:plain

まとめ

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

*1:基本Travisから叩くときにしか使用しないので、ローカルにはインストールしていません。

*2:h-miyako.hatenablog.com

*3:Educationプランの問い合わせをするとプライベートリポジトリの数を尋ねられたので、学生でプライベートリポジトリでも使いたい〜って場合は連絡してみると良いと思います。

*4:Building a Python Project - Travis CI

*5:trustyがダメでpreciseならokみたいなことは無いだろうと思って試していません