Python Advent Calendar25日目を担当します@giginetです。最終日なので遅れないように何とか書き上げました。
先日、Django向けに簡単なプラグインを開発しました。
そこで、実装したプラグインをPython2と3の違いを吸収して複数環境でテストするようにCI環境を構築したところ、ハマりどころが多かったため、この機会にまとめてみます。
この記事ではTravis CIを使って、複数のPythonバージョンにまたがるテストを実行して、カバレッジを計測する方法をご紹介します。
モジュールを実装する
まず、テストしたい簡単なモジュールを実装しましょう。今回はcalculator
モジュールにcalculator.py
を置き、Calculator
クラスを実装しました。
今回は全てのコードをPython3で実装することを想定しています。
class Calculator(object):
@staticmethod
def power(number):
return number * number
テストケースを実装する
次にテストケースを実装します。ここでは単純にCalculator#power
が動作するかをテストしています。
from unittest import TestCase
from calculator.calculator import Calculator
class CalculatorTestCase(TestCase):
def setUp(self):
self.calculator = Calculator()
def test_power(self):
self.assertEqual(self.calculator.power(12), 144)
Test Runnerを実装する
今度は記述したテストケースを探索して、テストを実行するスクリプトを記述します。
Python2.7以上であれば、以下のように簡単にテストケースを探索してテストを実行させることができます。
$ python -m unittest discover
Python2.6以下もテストする場合、標準のunittest
モジュールに、テストケースを探索するdiscover
が含まれていないため、代わりにunittest2
というパッケージを導入する必要があります。
unittest2
$ pip install -r requirements-test.txt
unittest2
を用いて、以下のようにテストケースを探索して実行するスクリプトを実装しました。
#!/usr/bin/env python
import unittest2 as unittest
if __name__ == "__main__":
suite = unittest.TestLoader().discover('tests', pattern = "test_*.py")
unittest.TextTestRunner().run(suite)
これを実行すると下記のようにテストが走ります。
$ chmod +x runtest.py
$ ./runtest.py
.
----------------------------------------------------------------------
Ran 1 tests in 0.001s
OK
toxを使って複数のバージョンのPythonでテストする
次はいよいよ、このテストケースを複数の環境で走らせてみましょう。
一つのソースを複数バージョンのPythonやDjangoで簡単にテストする方法として、 toxを利用することがよく知られています。
Welcome to the tox automation project — tox 2.3.1 documentation
toxは様々な条件を変えて、テスト環境を構築して、複数の環境でまとめてテストを実行することができるツールです。
tox.ini
という設定ファイルを定義することでテスト環境を構築することができるので、今回は以下のように定義します。
[tox]
skipsdist = True
envlist =
py26,
py27,
py33,
py34,
py35
[testenv]
deps=
-rrequirements-test.txt
commands=
./runtest.py
詳しくはドキュメントを参照してください。
tox configuration specification — tox 2.3.1 documentation
最後に、tox
を実行すると全てのテスト環境を同時に走らせることができます。
今回は動作確認のためにPython3.5でのみテストを実行してみます。
$ tox -r -e 'py35'
これで複数のPythonバージョンに対応したテスト環境の構築ができました。
tox-travisでTravis CI上でtoxを走らせる
今度はこれをTravis CI上で走らせてみましょう。
今回はtox-travis
というモジュールを使います。
これを導入すると、Travis CI上でtoxを動かすための設定を省略することができます。
例えば下記のように.travis.yml
を用意するだけで複数の環境でテストが実行できます。
sudo: false
language: python
python:
- 2.6
- 2.7
- 3.3
- 3.4
- 3.5
install:
- pip install tox tox-travis
- pip install coverage coveralls
script:
- tox -r
これをTravis CI上で実行すると、以下のように複数のコンテナが立ち上がり、簡単にテストすることができます。便利ですね。
__future__
モジュールを使ってPython2と3の互換性を保つ
Python3向けに書いたコードをPython2で動かすためには、__future__
モジュールなどを使い、後方互換性を保った書き方をしなくてはなりません。
この記事では詳しく述べないため、詳しくはドキュメントを参照してください。
Easy, clean, reliable Python 2/3 compatibility — Python-Future documentation
Python2でmockテスト
モックやスタブを使ったテストを行いたい場合、Python3には標準ライブラリにmockモジュールが含まれています。
26.5. unittest.mock — mock object library — Python 3.5.1 documentation
一方でPython2以前の環境においてはmockを標準で使うことができません。
Python3のmockモジュールを2以前にバックポートしたパッケージが存在するので、それを導入しましょう。
mock
まずunittest.mock
がimportできるかを試し、標準ライブラリに含まれていなかった場合、mock
ライブラリからimportするようにしましょう。
from unittest import TestCase
try:
from unittest.mock import MagicMock
except ImportError:
from mock import MagicMock
class CalcTestCase(TestCase):
def test_mock(self):
mock_object = MagicMock()
mock_object.__str__.return_value = 'hello'
self.assertEqual(str(mock_object), 'hello')
このようにすると、Python2でも3同様にmockテストを行うことができます。
mockの詳しい使い方については以下を参照すると良いでしょう。
Python - もっくもっく~unittest.mock, mock.mock revisit~ - Qiita
カバレッジを計測する
全ての環境でテストが通るようになったら、最後にカバレッジを計測して、coverailsで管理しましょう。
まず、tox.ini
で以下のようにcoverage
を実行してカバレッジを計測します。
deps=
-rrequirements-test.txt
coverage
commands=
{envbindir}/coverage run --append --source=calculator runtest.py
このとき、.coveragerc
というファイルを定義しておくと、カバレッジ計測のルールを設定することができます。以下では、カバレッジ計測を行わないファイルや行を定義しています。
詳しくは以下をご覧ください。
Configuration files — Coverage.py 4.0.3 documentation
[report]
include =
calculator/*.py
omit =
calculator/__init__.py
exclude_lines =
pragma: no cover
if __name__ == .__main__.:
最後に.travis.yml
に以下を追記し、テスト終了後にcoveralls
にログを送るようにします。
install:
- pip install tox tox-travis
- pip install coverage coveralls
after_success:
- coverage report
- coveralls
これでCoverallsでカバレッジを記録できるようになりました。
まとめ
この記事では、Pythonのライブラリを複数バージョンでテストする方法について簡単にまとめました。
今回紹介したTest Suiteを以下のリポジトリに簡単にまとめてあります。
また、tox
を上手く使えば、あるライブラリについて複数のバージョンでテストすることもできます。
以下のDjangoプラグインでは、複数バージョンのPythonとDjangoの組み合わせでテストしています。
ご興味のある方はぜひ参考にしてみてください。