これだけ知っていればPythonのテストコードが書ける! 〜超入門編〜


この記事を読んだらどうなれるか

この記事を読み終わった後に、読者が以下の三つを達成できるような記事になっています。

1.ソースコードをテストすることの意義を理解する
2.Pythonのテストコードをどのように書くのか、雰囲気をつかむ
3.Pythonによるテストコードの書かれたファイルを単一で実行できる

実行環境

OS: macOS Mojava 10.14.6
Pythonバージョン: 3.7.1

テストコードを書く意義

テストはソースコードの品質を保証するために書きます。

バグのない完璧なソースコードを書きました〜と言われて渡されたソースコードを信用できるでしょうか。
全くミスしない人がいるとしたら、信用してもいいのかもしれません。しかし、人は往々にしてミスするものです。

テストがある程度書かれていれば、ソースコードの品質を信用することができます
また、テストがあれば、ソースコードが正しく作られているかを判断する基準として利用することもできます

それではPythonでテストを書いていきましょう!

最大公約数を算出するメソッドのテストを書く

1. テストするメソッドの仕様

テストする対象として、最大公約数を算出するメソッドを考えます。
最大公約数を英語で言うとGreatest Common Divisorです。頭文字をとってファイル名をgcd.pyとしましょう。

gcd.py
def gcd(a, b):
    # なんかしてa,bの最大公約数を算出
    return # 戻り値は最大公約数

gcd()の引数a,bは整数が入力されることを想定するものとし、戻り値としてa,bの最大公約数を返します。

テストする関数の仕様が固まったところで、テストコードを書いていきましょう。

2. テストコードを書くためにimportするのはこれ!

今回、Pythonでテストコードを記述するために使用するのは、unittestと呼ばれるモジュールです。
標準搭載のため、インストールする必要はありません。
早速importしてみましょう。

test_gcd.py
import unittest
from gcd import gcd 

テスト対象であるgcd()をテストするためにgcd.pyの関数gcdもインポートしておきましょう。
これでテストコードを書く準備が整いました。

3. テストケースを作成しよう

次にテストケースを作成していきます。

テストケースというのは、あるモジュールや、あるクラスに備わっている機能が正しく動作することを確認するために入力と、入力から想定される出力を一つ以上定義したものです。

まずはテストケースを内包するクラスを作成します。

test_gcd.py
import unittest
from gcd import gcd 

class GCDTest(unittest.TestCase):
    pass

テストケースはunittestモジュールのTestCaseクラスを継承して宣言します

unittest.TestCaseを継承することによって、テストケースを作成する上で必要なメソッド類を利用できるようになります。

クラス名(テストケース名)は、最大公約数をテストするものなので、GCDTestにしてみました。

4. テストを書いてみる

それでは最大公約数を算出するメソッドのテストを書いてみましょう。

test_gcd.py
import unittest
from gcd import gcd 

class GCDTest(unittest.TestCase):
    def test_gcd(self):
        self.assertEqual(15, gcd(30, 15))
        pass

test_gcdと呼ばれるメソッドを実装しました。

今回は二つの整数の最大公約数を算出するメソッドを実装したいのですから、a = 30, b = 15が入力されたのであれば、戻り値は15にならなければなりません。

これをチェックしているのが、self.assertEqual(15, gcd(30, 15))です。
gcd(30, 15)の結果が15であることを確認しています。

assertという英語は、「断言する」というような意味を持つ単語であることを踏まえると、理解が容易になります。
self.assertEqual(15, gcd(30, 15))は、「gcd(30,15)の結果は15と等しくなる」ということを断言しているのです。わかりやすいですね。

5. テストを実行してみよう

テストの実行には以下のコマンドを利用します。

python -m unittest `作成したモジュール名`

今回作成したモジュールは、test_gcd.pyです。
test_gcd.pyを指定して、実行してみます。

python -m unittest test_gcd.py

実行結果

失敗していますが、実装していないので当然ですね。
どこでテストが失敗したのか、実行されたテストの個数、実行時間などが示されます。

gcdメソッドを実装してもう一度実行してみましょう。

gcd.py
def gcd(a, b):
    return b if a%b == 0 else gcd(b, a%b)

実装についてはユークリッドの互除法というのを使っています。ユークリッドの互除法は、最大公約数を高速に求められるアルゴリズムです。
ここでは触れませんので、気になる人は調べてみてください。

それでは、実装できたところで、python -m unittest test_gcd.pyを実行!

テストが通過したため、OKと表示されました。やったね。

テストを書く上で注意すること

今回書いたテストは、self.assertEqual(15, gcd(30, 15))だけでした。
これは、テストケースとして十分でしょうか。

答えは否です。一つだけのチェックで対象のメソッドが正しく実装できているとは言えません。
なぜなら、今回のチェック項目だけでは以下のような実装でもテストケースを通過してしまうからです。

gcd.py
def gcd(a, b):
    return 15

これではどんな入力だったとしても最大公約数は15であるということになってしまいます。

ケーステストを書く際には、入力と出力のパターンにどのようなものがあるのかを考え、網羅するように心がけましょう

まとめ

・テストはソースコードの品質を保証するために記述する
・Pythonのテストを作成するのにはunittestモジュールを使用する
・テストケースを作成するときは、unittest.TestCaseを継承する。
・テストケースとは、入力と、入力から想定される出力のあつまり
・python -m unittest モジュール名で該当ファイルのテストケースを実行できる
・テストケースは入力と出力のパターンにどのようなものがあるのかを考え、十分に網羅するようにする

次の記事

本記事の内容は、とにかく雰囲気を掴む、という観点で書きました。
そのため、細かいお作法などについて触れられていません。

これについては、次の記事で補足していく予定です。

Coming Soon!