ウェブサイト検索

Python 単体テスト - 単体テストの例


今日はPythonのunittestについて学び、Pythonのユニットテストの例プログラムを見ていきます。前回のチュートリアルでは、Pythonのzip関数について学びました。

Pythonのユニットテスト

Pythonのunittestモジュールは、ソースコードのユニットをテストするために使用されます。プロジェクトをテストする必要があるとします。関数がどのようなデータを返すかはわかっています。膨大なコードを書いた後、出力が正しいかどうかを確認する必要があります。通常、私たちが行うことは、出力を印刷して、参照出力ファイルと照合したり、出力を手動で確認したりすることです。この手間を減らすために、Pythonはunittestモジュールを導入しました。このモジュールを使用すると、簡単なコードで関数の出力を確認できます。このチュートリアルでは、Pythonのunittestモジュールの基本的な使い方と、クラスの関数をテストするためのいくつかのPythonユニットテストケースについて説明します。

Pythonユニットテスト例ソース

まず第一に、ユニットテスト用にいくつかのコードを書く必要があります。Pythonのクラスを作成します。このクラスの主な目的は、人物の名前を保存し、取得することです。そのため、データを保存するためのset_name()関数と、クラスから名前を取得するためのget_name()関数を書きます。

class Person:
    name = []

    def set_name(self, user_name):
        self.name.append(user_name)
        return len(self.name) - 1

    def get_name(self, user_id):
        if user_id >= len(self.name):
            return 'There is no such user'
        else:
            return self.name[user_id]


if __name__ == '__main__':
    person = Person()
    print('User Abbas has been added with id ', person.set_name('Abbas'))
    print('User associated with id 0 is ', person.get_name(0))

Person.pyというクラスファイルを名付けました。そして、上記のコードの出力は以下のようになります。

python3.6 Person.py 
User Abbas has been added with id  0
User associated with id 0 is  Abbas
$

Pythonユニットテストの構造

では、ユニットテストのためのコードの書き方を学びましょう。個々のテストケースは unittest.TestCase をサブクラス化することで作成されます。適切な関数をオーバーライドまたは追加することで、テストの論理を追加できます。次のコードは、aがbと等しい場合に成功します。

import unittest


class Testing(unittest.TestCase):
    def test_string(self):
        a = 'some'
        b = 'some'
        self.assertEqual(a, b)

    def test_boolean(self):
        a = True
        b = True
        self.assertEqual(a, b)

if __name__ == '__main__':
    unittest.main()

Pythonのunittestモジュールを実行する方法

PyCharm IDEを使用している場合は、単にctrl+shift+F10を押すことでunittestモジュールを実行できます。そうでない場合は、コマンドプロンプトを使用してこのモジュールを実行できます。例えば、単体テスト用のファイル名をBasic_Test.pyとしました。したがって、Python unittestを実行するためのコマンドは次のようになります:$python3.6 -m unittest Basic_Test.Testing 詳細情報を表示したい場合は、コマンドは次のようになります;$python3.6 -m unittest -v Basic_Test.Testing PyCharmを使用することで、以下の出力を得ることができます。

Pythonユニットテストの結果と基本的な機能

このユニットテストには3つの可能な結果があります。それらは以下に示されています:

  1. OK: すべてのテストケースが合格した場合、出力はOKと表示されます。
  2. 失敗: いずれかのテストケースが失敗し、AssertionError例外を発生させた場合
  3. エラー: AssertionError 以外の例外が発生した場合。

unittestモジュールにはいくつかの関数があります。それらは以下に示されています。

方法チェックがそれを確認します
aとbが等しいことを確認するa==b
aとbが等しくないことを確認するa は b ではない
xが真であることを確認するbool(x) は真です
xが偽であることを主張する(false)bool(x) は False です
assertIs(a,b)a は b です
assertIs(a,b)a は b です
assertIsNot(a, b)aはbではない
xがNoneであることを確認するx は None です
xがNoneでないことを確認するxはNoneではありません
aがbに含まれていることを確認するbの中のa
aがbに含まれないことを確認するbにないa
assertIsInstance(a, b)aがbのインスタンスであるかどうかを判断します。
assertNotIsInstance(a, b)aがbのインスタンスでない

Pythonユニットテストの例

今、私たちのソースクラスPersonのユニットテストを書く時間です。このクラスには、get_name()とset_name()という二つの関数が実装されています。これから、unittestを使用してこれらの関数をテストします。これらの二つの関数に対して二つのテストケースを設計しました。以下のコードを見てみてください。すぐに理解できるでしょう。

import unittest

This is the class we want to test. So, we need to import it
import Person as PersonClass


class Test(unittest.TestCase):
    """
    The basic class that inherits unittest.TestCase
    """
    person = PersonClass.Person()  # instantiate the Person Class
    user_id = []  # variable that stores obtained user_id
    user_name = []  # variable that stores person name

    # test case function to check the Person.set_name function
    def test_0_set_name(self):
        print("Start set_name test\n")
        """
        Any method which starts with ``test_`` will considered as a test case.
        """
        for i in range(4):
            # initialize a name
            name = 'name' + str(i)
            # store the name into the list variable
            self.user_name.append(name)
            # get the user id obtained from the function
            user_id = self.person.set_name(name)
            # check if the obtained user id is null or not
            self.assertIsNotNone(user_id)  # null user id will fail the test
            # store the user id to the list
            self.user_id.append(user_id)
        print("user_id length = ", len(self.user_id))
        print(self.user_id)
        print("user_name length = ", len(self.user_name))
        print(self.user_name)
        print("\nFinish set_name test\n")

    # test case function to check the Person.get_name function
    def test_1_get_name(self):
        print("\nStart get_name test\n")
        """
        Any method that starts with ``test_`` will be considered as a test case.
        """
        length = len(self.user_id)  # total number of stored user information
        print("user_id length = ", length)
        print("user_name length = ", len(self.user_name))
        for i in range(6):
            # if i not exceed total length then verify the returned name
            if i < length:
                # if the two name not matches it will fail the test case
                self.assertEqual(self.user_name[i], self.person.get_name(self.user_id[i]))
            else:
                print("Testing for get_name no user test")
                # if length exceeds then check the 'no such user' type message
                self.assertEqual('There is no such user', self.person.get_name(i))
        print("\nFinish get_name test\n")


if __name__ == '__main__':
    # begin the unittest.main()
    unittest.main()

unittestモジュールは、テスト関数を定義された順序ではなく、名前の順序で実行することに注意してください。そして、私たちのset_nameテストを最初に実行したいので、テストケースの関数名をtest_0_set_nameとtest_1_get_nameというように名付けました。

Pythonユニットテスト例出力

以下の画像は、通常モードと詳細モードの両方で私たちのユニットテストプログラムによって生成された出力を示しています。

python3.6 -m unittest -v PersonTest.Test
test_0_set_name (PersonTest.Test) ... Start set_name test

user_id length =  4
[0, 1, 2, 3]
user_name length =  4
['name0', 'name1', 'name2', 'name3']

Finish set_name test

ok
test_1_get_name (PersonTest.Test) ... 
Start get_name test

user_id length =  4
user_name length =  4
Testing for get_name no user test
Testing for get_name no user test

Finish get_name test

ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
$

これがPythonのunittestチュートリアルについてのすべてです。もっと学ぶには、公式ドキュメントを読んでください。さらに質問がある場合は、コメントボックスを使用してください。 :)

関連記事: