目次
- 目次
- はじめに
- アサーション
- ユニットテスト
- doctest
- テストカバレージ
- 静的コード解析
- 自動でコードをスタイルガイド通りに修正する autopep8
- 最後に一言
- 参考資料
- MyEnigma Supporters
はじめに
ロボティクス用のソフトを書いていると、
ソフトウェアテストは無視しがちになりますが、
やはり長期的にメンテナンスしやすいソフトを作るには、
ソフトウェアテストは重要だと思います。
今回は、Pythonにおける
ソフトウェアテスト関連の方法をメモしておきたいと思います。
改めてまとめると、意外と知らないことが多かったです。
アサーション
一番簡単なテストは、実行時テストでアサーションを使うことかと思います。
アサーションは、ソフトウェアの仕様として、
絶対に起きないような条件が発生した時にプログラムを終了 or 例外を投げる仕組みです。
アサーションとは|assertion|アサート - 意味/解説/説明/定義 : IT用語辞典
後述のユニットテストのように、複雑なテストなどは難しいことが多いですが、
いちいちテストコードを走らせなくても、アプリを起動すれば自動でテストされますし、
コードのドキュメントとして有用なので、
個人的には一番好きなテスト方法です。
コードコンプリートでもアサーションは有効な方法として上げられていました。
Pythonでアサーションを使いたい場合には、
標準でassert文が使えるので、これを使えば良いかと思います。
6.2 Assert 文 (assert statement)
assert文は、
assert 条件式,"エラーコメント"
といった感じで使います
例えば、次のようなコードを起動すると、
# --*-- coding:utf-8 --*-- """ assert sample """ a=5; b=3 assert a>b,"a is bigger" assert a<b,"b is bigger"
こうなります。
つまり、仕様的に必ず発生しない条件を
条件式に入れれば、
自動的に実行時テストを実施してくれるのです。
ユニットテスト
各関数(モジュール)をテストするユニットテストに関しては、
標準ライブラリのunittestを使った方が良いと思います。
25.3. unittest — ユニットテストフレームワーク — Python 2.7ja1 documentation
例えば、下記のような算数モジュールをテストしたい場合、
# --*-- coding:utf-8 --*-- """ func.py Simple Math module """ def Add(a,b): """ 足し算関数 """ return a+b def Substract(a,b): """ 引き算関数 """ return a-b
test*.py(スターの部分は任意の名前)の
テスト用スクリプトファイルを同じディレクトリに作って、
(またはパスが通ったテスト用ディレクトリにファイルを置いて)
下記のようにテストコードを作ります。
# --*-- coding:utf-8 --*-- """ Test Function """ import unittest import func class Test(unittest.TestCase): """ Unit Test Class """ def test_Add(self): value=func.Add(1,5) self.assertEqual(6,value) def test_Substract(self): value=func.Substract(5,3) self.assertEqual(2,value)
あとは、テストファイルがあるディレクトリで、
下記のコマンドを入力すると、
勝手にテスト関数を探して、テストを実施してくれます。
python -m unittest discover
成功した場合:
失敗した場合:
doctest
基本的なユニットテストは、unittestを使えば良いですが、
Pythonにはdoctestという面白いテストフレームワークがあります。
25.2. doctest — 対話的な実行例をテストする — Python 2.7ja1 documentation
python楽しいよ? (python+doctest+vim=ヒャッハー) | Zafiel
doctestは各関数のドキュメンテーション文字列(docstring)を元に、
テストを実施してくれる標準モジュールです。
Pythonの場合、関数の宣言のすぐ下に書かれたコメントは、
ドキュメンテーション文字列として自動で認識されます。
例えば、下記のようにコードを書いて、
関数の宣言のすぐ下のコメントに期待する出力をコメントとして書きます。
そして、doctestをインポートして、testmod()でテストを実施します。
# --*-- coding:utf-8 --*-- """ doctest sample """ def Add(a, b): """ add func Test >>> Add(1, 3) 4 """ return a+b if __name__ == "__main__": import doctest doctest.testmod()
すると問題が無い場合何も表示されないので、-vをつけるとテスト結果が表示されます。
あえて失敗させると、
こうなります。
doctestは、テストを実施しながらも、
ソースのドキュメント作成もできるのがメリットです。
ただ、あまり複雑なテストは難しそうなので、
下記の記事のように
unittestを組み合わせてテストすると便利かと思います。
doctestの実行方法について - 主にプログラムを勉強するブログ
テストカバレージ
テストがどれだけ網羅的に実施されたかをチェックする
テストカバレージのシステムを導入しておくと、
テスト文化が根付きやすくなると思います。
情報システム用語事典:テストカバレッジ(てすとかばれっじ) - ITmedia エンタープライズ
pythonでは、coverageというパッケージが便利みたいです。
Index of Packages : Python Package Index
Python の Nose + coverage でユニットテストのカバレッジを確認する | CUBE SUGAR STORAGE
PythonでUnitTestとCodeCoverageをやってみる(2) - HDEラボ
coverageは標準ライブラリではないので、
pipでインストールしましょう。
sudo pip install coverage
先ほどのユニットテストの結果のカバレージを見たい場合は、
先ほどのコマンドのpython以下をそのまま入れて、
下記のようにrunコマンドを入力します。
coverage run -m unittest discover
するとそのディレクトリに.coverageの隠しフォルダができるので、
reportコマンドでカバレージが表示されます。
coverage report
続いて、ソフトのどの部分がテストされていないのかを
チェックする場合は、annotateコマンドを使います。
coverage annotate
すると、*,coverファイルができるので、
catやvimで開くと、下記のようにテストしない部分を
ビックリマークで表してくれます。
(あえて先ほどのテストコードのtest_Substractをコメントアウトした場合です)
しかし、自分の環境では日本語が入っているとエラーが出てしまいました(泣)
残念です。python2.x系を使っているからかな。
レポートの結果をhtmlで見たい場合は、
htmlコマンドを使います。
coverage html
すると、htmlcovディレクトリができるので、
その中のindex.htmlを開くとかっこいいレポートが見れます。
人力でカバレージを見る場合はreportコマンドで十分かと思いますが、
自動でレポートを作ったりできますし、
ソートやフィルタの機能も付いていたので大規模なカバレージを解析するのに便利です。
最後にeraseコマンドを使うと、.coverageのファイルを消せます。
ただ、htmlcovや*,coverファイルは消えませんでした。
静的コード解析
テストは通っても、他の人が読みにくかったり、
使われない変数などがあると、コードの品質が良いとは言えません。
そこで重要なのが、静的コード解析です。
静的コード解析は、ソースコードを機械的にチェックして、
コードの複雑性や、コード規約に即しているのかをチェックしてくれます。
実行しないソフトウェアテストとも言えるでしょう。
Pythonでの静的コード解析は、flake8というパッケージが良いようです。
JenkinsでPythonを静的解析(flake8) - Any Any
Jenkins + Warnings Plugin + flake8 で静的解析 - Hack like a rolling stone
このflake8はPythonの標準的なコードスタイルガイドであるPEP8に
コードが準拠しているかどうかを自動でチェックしてくれます。
こちらも標準ではないのでpipでインストールします。
sudo pip install flake8
使用方法は簡単で、
flake8コマンドの後に解析したいPythonスクリプトのパスを書けばOKです。
例えば、先ほどの算数モジュールfunc.pyを解析すると、
flake8 func.py
となりました。
幾つかエラーが出ていますが、
E302 expected 2 blank lines, found 1は関数などの間には2つ空行を入れろ。
E231 missing whitespace after ','は、引数のカンマの後ろのスペースを入れる。
W391 blank line at end of fileはファイルの最後に空行を入れるな。
なのでそれらを直したら何も言われなくなりました。
先ほどの参考リンクにもありますが、
コミットする毎にJenkinsなどで、
静的解析するようにすれば、
常に綺麗なコードが保てますね。
自動でコードをスタイルガイド通りに修正する autopep8
先ほどのflake8はチェックして、エラー発報してくれるだけですが、
autopep8というツールを使うと、
自動でコードをPEP8に準拠するように修正してくれます。
インストールは、
pip install --upgrade autopep8
でOKです。
あとは、
autopep -i hoge.py
でhoge.pyが自動で綺麗にフォーマットされます。
-iのオプションを付けるとファイルの中身を修正し、
オプションが無い場合は標準出力に
フォーマット後のコードが出力されるだけになります。
また、通常のモードだと、
x == Noneを x is Noneなどに変換してくれないので、
よりアグレッシブにコードを修正してもらいたい時には、
autopep8 --aggressive hoge.py
とすると、より厳しいフォーマットが実施されます。
ちなみにaggressiveオプションは複数繋げることで、
より厳しいフォーマットがされます。
autopep8 --aggressive --aggressive hoge.py
最後に一言
Pythonのツール群は最高に便利ですねー。
C++ももっと簡単にテストできればいいのにな。
参考資料
MyEnigma Supporters
もしこの記事が参考になり、
ブログをサポートしたいと思われた方は、
こちらからよろしくお願いします。