MyEnigma

とある自律移動システムエンジニアのブログです。#Robotics #Programing #C++ #Python #MATLAB #Vim #Mathematics #Book #Movie #Traveling #Mac #iPhone

初心者のためのPythonにおけるソフトウェアテスト

目次

はじめに

ロボティクス用のソフトを書いていると、

ソフトウェアテストは無視しがちになりますが、

やはり長期的にメンテナンスしやすいソフトを作るには、

ソフトウェアテストは重要だと思います。

myenigma.hatenablog.com

 

今回は、Pythonにおける

ソフトウェアテスト関連の方法をメモしておきたいと思います。

改めてまとめると、意外と知らないことが多かったです。

アサーション

一番簡単なテストは、実行時テストでアサーションを使うことかと思います。

アサーションは、ソフトウェアの仕様として、

絶対に起きないような条件が発生した時にプログラムを終了 or 例外を投げる仕組みです。

アサーションとは|assertion|アサート - 意味/解説/説明/定義 : IT用語辞典

後述のユニットテストのように、複雑なテストなどは難しいことが多いですが、

いちいちテストコードを走らせなくても、アプリを起動すれば自動でテストされますし、

コードのドキュメントとして有用なので、

個人的には一番好きなテスト方法です。

コードコンプリートでもアサーションは有効な方法として上げられていました。

myenigma.hatenablog.com

 

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"

こうなります。

f:id:meison_amsl:20150523162453p:plain

つまり、仕様的に必ず発生しない条件を

条件式に入れれば、

自動的に実行時テストを実施してくれるのです。

ユニットテスト

各関数(モジュール)をテストするユニットテストに関しては、

標準ライブラリのunittestを使った方が良いと思います。

単体テスト - Wikipedia

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

成功した場合:

f:id:meison_amsl:20150523101729p:plain

失敗した場合:

f:id:meison_amsl:20150523101948p:plain

doctest

基本的なユニットテストは、unittestを使えば良いですが、

Pythonにはdoctestという面白いテストフレームワークがあります。

25.2. doctest — 対話的な実行例をテストする — Python 2.7ja1 documentation

python楽しいよ? (python+doctest+vim=ヒャッハー) | Zafiel

モジュールやテストファイルの doctest

 

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をつけるとテスト結果が表示されます。

f:id:meison_amsl:20150523171609p:plain

あえて失敗させると、

f:id:meison_amsl:20150523171730p:plain

こうなります。

doctestは、テストを実施しながらも、

ソースのドキュメント作成もできるのがメリットです。

ただ、あまり複雑なテストは難しそうなので、

下記の記事のように

unittestを組み合わせてテストすると便利かと思います。

doctestの実行方法について - 主にプログラムを勉強するブログ

テストカバレージ

テストがどれだけ網羅的に実施されたかをチェックする

テストカバレージのシステムを導入しておくと、

テスト文化が根付きやすくなると思います。

情報システム用語事典:テストカバレッジ(てすとかばれっじ) - ITmedia エンタープライズ

 

pythonでは、coverageというパッケージが便利みたいです。

Index of Packages : Python Package Index

Python の Nose + coverage でユニットテストのカバレッジを確認する | CUBE SUGAR STORAGE

PythonでUnitTestとCodeCoverageをやってみる(2) - HDEラボ

Pythonでカバレッジを調べる - Qiita

 

coverageは標準ライブラリではないので、

pipでインストールしましょう。

sudo pip install coverage

 

先ほどのユニットテストの結果のカバレージを見たい場合は、

先ほどのコマンドのpython以下をそのまま入れて、

下記のようにrunコマンドを入力します。

coverage run -m unittest discover

 

するとそのディレクトリに.coverageの隠しフォルダができるので、

reportコマンドでカバレージが表示されます。

coverage report

f:id:meison_amsl:20150523111554p:plain

 

続いて、ソフトのどの部分がテストされていないのかを

チェックする場合は、annotateコマンドを使います。

coverage annotate

すると、*,coverファイルができるので、

catやvimで開くと、下記のようにテストしない部分を

ビックリマークで表してくれます。

(あえて先ほどのテストコードのtest_Substractをコメントアウトした場合です)

f:id:meison_amsl:20150523112620p:plain

しかし、自分の環境では日本語が入っているとエラーが出てしまいました(泣)

残念です。python2.x系を使っているからかな。

 

レポートの結果をhtmlで見たい場合は、

htmlコマンドを使います。

coverage html

すると、htmlcovディレクトリができるので、

その中のindex.htmlを開くとかっこいいレポートが見れます。

f:id:meison_amsl:20150523113326p:plain

人力でカバレージを見る場合はreportコマンドで十分かと思いますが、

自動でレポートを作ったりできますし、

ソートやフィルタの機能も付いていたので大規模なカバレージを解析するのに便利です。

 

最後にeraseコマンドを使うと、.coverageのファイルを消せます。

ただ、htmlcovや*,coverファイルは消えませんでした。

静的コード解析

テストは通っても、他の人が読みにくかったり、

使われない変数などがあると、コードの品質が良いとは言えません。

そこで重要なのが、静的コード解析です。

静的コード解析 - Wikipedia

静的コード解析は、ソースコードを機械的にチェックして、

コードの複雑性や、コード規約に即しているのかをチェックしてくれます。

実行しないソフトウェアテストとも言えるでしょう。

 

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

f:id:meison_amsl:20150523143649p:plain

となりました。

幾つかエラーが出ていますが、

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.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

MyEnigma Supporters

もしこの記事が参考になり、

ブログをサポートしたいと思われた方は、

こちらからよろしくお願いします。

https://gumroad.com/l/myenigmasupportersgumroad.com