よちよちpython

独習 python/Qpython/Pydroid3/termux/Linux

Pythonユニットテスト?超基本

バグにも負けず、エラーにも負けず、うんたらかんたら。

プログラマーの仕事の99%はバグの除去作業だ」と外国の有名(らしい)プログラマーが言うておりました。
プログラムの書かれたただのテキストファイルが何MBもあったりするわけですから、何十万行と書いてあるのでしょう、大変な作業だろうと思います。
こちとら、ろくにプログラムの書き方も知らんのが作る、たった数~数十行しかないポンコツプログラムですらエラーがでたり狙いと違う動きをしてウンウン唸るのに。



で、エラーの場合は「何行目が何々の為にエラーですよ」と出ますけど、エラーも出ず思ったものと違う振る舞いをされると、何処が間違っているのか調べるのは見つけにくい。



何か良い方法はないかね、明智君。

っちゅうことで、今回はunittest ユニットテストなるものを触ってみようかと思います。

タイトルに「超基本」とか書いてますが、テスト用のコードにはclassが使ってあって、初心者にはぶっちゃけ厳しい。クラスやオブジェクト指向なるプログラミング方法について入門書にはあまり触れられていなく、かつ発想を理解するのはちょっと難しい。
使わないと覚えませんので、そういうつもりで行きます。



目次



作業環境


Androidスマホ
termux
Python3.7



ところでユニットテストってなぁに?


Python公式 unittest
pytest公式

Pythonでは標準装備された前者のunittestや、外部ライブラリの後者pytestなどのツールを使って、プログラムを構成する比較的小さな単位(ユニット)が正しく動くかどうかを検証するテストのこと。
全体的な不具合をテストするときはまた別のやり方をするようです。



関数に引数を渡すと、関数が処理して、returnで戻り値が返って来る。
このとき、関数(ある引数)→期待する戻り値が本当に得られるかどうかを確める手段と思っていいんでしょうか。



とりあえず使ってみる


標準で入っているモジュールunittestを使います。インストール不要。



準備するもの


  1. ユニットテストしたいpyファイル
  2. 1をテストする為のpyファイル


この2つ。



1.テスト対象ファイル


↓こちらのファイルをご用意致しました。

str型の引数の全てが数字なら真、そうでなければ偽を返すisdigit関数による判別の関数。

def checkDigit(num):
    if num.isdigit():
        return "合格"
        
    else:
        return "不合格"

chkDgt.pyというファイル名で保存することにします。



2.ユニットテストファイル


↑のchkDgt.pyをテストする為のファイルを別個に作ります。

test_chkDgt.pyという名前で保存することにします。

import unittest   # 1)
import chkDgt # 2)

class TestCheckDigit(unittest.TestCase): # 3)
    
    def test_checkDigit(self):  # 4)
        
        num = "10"  #  5)  
        actual = "合格" # 6)
        
        result = chkDgt.checkDigit(num) # 7)  
        
        self.assertEqual(actual,result)   # 8)
        
if __name__ == "__main__":
    unittest.main()

↑の説明

1) 標準モジュールのunittestをインポート。

2) テスト対象ファイルをインポート。

3) unittest.TestCaceクラスの子クラス定義、名前適当でOK(MyTestとか)、()内は決まり文句。

4) メソッド名はテスト対象の関数名の前にtest_を付ける。(self)は決まり文句。

5) テスト対象に渡す引数を適当に。

6) 5の引数に対して期待するテスト対象の関数の戻り値

7) 検証のため変数にする。

8) 検証。(期待する答え,関数に引数を入れたときの答え)が等しいかどうか。()内以外は決まり文句



unittest.main()を実行すると、テスト対象コード内でunittest.TestCaseを継承した全てのクラスを認識し、そのメソッドの中でtest何とかな名前のものが全てテスト実行される。



ユニットテストの実行


テストの実行はターミナルからコマンドで行う。

python test_chkDgt.py


テストファイル test_chkDgt.py の最後にif __name__=="__main__"とその下の行のunittest.main()を書いていない場合は次のコマンドを実行する。↓

python -m unittest test_chkDgt.py


一番最後の.pyは無くても大丈夫みたい。

python -m unittest test_chkDgt
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK



おわりに(早々と)


あとで気づいたが、同じisdigit()ユニットテストされているプログラム教育サイトがありますね…。

実はテスト実行で「ok」が出るまでに何度もエラーを出しては書き直した。テスト対象のコードは単純なものですけどw
参考に見ているコードはもっと複雑なもので、テストのコードを見ても正直なにをしているのかよくわからんですたい。



疑問点として、今回は引数と戻り値を1対しか使ってませんが、これで確かに動くコードだと言えるのか。



それから、テストコードを書く手間が掛かるなぁと。検索すると「うちの職場はテストする環境や習慣がない」というプログラマの方々が普通によくいるようですし。これを自動化できれば、動くプログラム本体の自動生成自体もできてしまうんだろうか。



ユニットテストには前準備後片付けとか、その他多くのメソッドがある。また別の機会に。