Python doctest

Python学習【365日チャレンジ!】172日目のマスターU(@Udemy11)です。

ようやく青空が広がる天気が続くようになり、夏が来たか〜!って感じですが、今度は暑さで体が溶けてしまいそうです。

夜も寝苦しいので、エアコンは必須アイテムですが、温暖化問題があるので、果たしてこのままエアコンを使い続けても大丈夫なのか心配になってしまいます。

極力エアコンを使わないで扇風機で対応しようとしてはいるのですが、熱帯夜でエアコンつけないのはある意味修行のようなものです。

それでは今日もPython学習を始めましょう。

昨日の復習

昨日は、作図ができちゃうnetworkxを学習しました。

グラフ描写ができるmatplotlibと合わせて使うことで、様々なつながりをグラフに表すことができました。

点(node)を作成して、点と点との関係をつなげることで点と線で、三角形を描写しましたが、いろいろなサンプルコードをコピーして複雑なグラフも描写できました。

詳しくは、昨日の記事をごらんください。

今日からテストセクションに入ります。

テストとは

プログラミングにおけるテストとは、ソフトウェアテストのことで、プログラムから欠陥(バグ)を見つけて、そのバグを修正する作業につなげるものです。

なので、世の中に出回っているアプリはすべてこのソフトウェアテストを乗り越えてリリースされているわけですが、すべての状況に対応したテストを行うことができないため、リリース後もアップデートが繰り返されて、バグが修正されているわけです。

例えばiOSなどは、世界中の人に利用されているので、世界中の人が使う想定のテストはできません。

なので、新しいバージョンをリリースしてから、世界中の人が使い始めることでいろいろなエラーが起こってきます。
そのエラーをできるだけ起こらないようにするのがテストですが、世の中には悪いことをしようとする人もたくさんいるわけで、完璧なテストはリリース前にできているわけではないということです。

今日からテストについての学習を行っていきますが、今日は非常に簡易なdoctestを学習します。

doctest

doctestは、対話的なPythonのセッション(足し算や掛け算などのように、値を渡して答えをもらう関数など)を実行して、その結果によって正常に終了するのか、例外処理をするかなどを指定することができます。

早速例を見ていきましょう。

class Cal(object):
    def add_and_double(self, x, y):
        """Add and double

        >>>  c = Cal()
        >>>  c.add_and_double(1, 2)
        4
        """
        result = x + y
        result *= 2
        return result

if __name__ == '__main__':
    import doctest
    doctest.testmod()

最初に、足し算をしてから2倍した結果を返す関数を持ったクラスのコードを書いています。("""で囲んだドキュメントの部分は後で説明します。)

13行目からがテストの部分ですが、このコード内でdoctestをインポートしているので、このPythonファイルがメインとして実行されたときだけdoctestが実行されるようになっています。

最初の関数に戻ってみると、"""で囲んで、ドキュメントを書いていますが、5行目6行目は、対話型シェルでオブジェクトを生成して、xに1を代入して、yに2を代入した関数を実行しています。

7行目に計算結果が4になることを想定しているので、それ以外の答えが返ってきたときは、次のような表示が返されます。

出力結果

**********************************************************************
File "/Users/bigmacpro/PycharmProjects/lesson/lesson.py", line 6, in __main__.Cal.add_and_double
Failed example:
    c.add_and_double(1, 2)
Expected:
    4
Got:
    6
**********************************************************************
1 items had failures:
   1 of   2 in __main__.Cal.add_and_double
***Test Failed*** 1 failures.

4だと指定されているのに、答えが6で計算されてます」というメッセージになっています。

もちろん計算結果は6が正しいので、先程のコードの46に変更すれば、メッセージは表示されずに正常に終了します。

エラーを扱う

次に先程のコードの変数xyには、数字が入るのが正しいのですが、こちらに文字列を入れてしまったときのエラー処理をテストしてみます。

class Cal(object):
    def add_and_double(self, x, y):
        """Add and double

        >>> c = Cal()
        >>> c.add_and_double('1', '2')
        6
        """
        if type(x) is not int or type(y) is not int:
            raise ValueError
        result = x + y
        result *= 2
        return result

以下省略

6行目で変数を文字列で指定しているので、結果は、9行目で引っかかってValueErrorが返されます。

出力結果

Failed example:
    c.add_and_double('1', '2')
Exception raised:
    Traceback (most recent call last):
      File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/doctest.py", line 1329, in __run
        compileflags, 1), test.globs)
      File "", line 1, in 
        c.add_and_double('1', '2')
      File "/Users/bigmacpro/PycharmProjects/lesson/lesson.py", line 10, in add_and_double
        raise ValueError
    ValueError
**********************************************************************
1 items had failures:
   1 of   2 in __main__.Cal.add_and_double
***Test Failed*** 1 failures.

コードの9行目、10行目で指定したValueErrorが返されています。

エラーを想定内にする

ValueErrorを想定内にして、処理を正常終了させるには、Pythonファイル7行目の6を次のコードに置き換えます。

Traceback (most recent call last):
...
ValueError

Exceptionの内容を...を使って中を省略して、最初と最後を記述することで、このエラーは想定内ということで、エラーが起こらずに正常終了するようになります。

使いみちがわからない

はっきりいうと、現時点で使いみちがわかりません。

意味合い的には、それぞれのモジュールを個別にテストすることで、問題がないかどうかを確認するためと、ドキュメンテーションなので、そのモジュールの使い方を解説するために用意しておくことでどんなことを想定しているのかがわかるということでしょうか?

使いみちがわからないというのも少し問題があるような気がしますが、後々使う場面に直面したときに思い出せるように頭の片隅に残しておこうと思います。

それでは、明日もGood Python!