【Python】unittestの使い方 実装・実行方法

2020年9月9日

この記事はPython標準のunittestの基本的な使い方についての備忘録です。

「テストコードの作成方法」「unittestの実行方法」について記載します。

はじめに

作業環境

今回は以下の環境でunittestを実装していきます。

Python 3.7.7
OS:windows
IDE:Visual Studio Code

テスト対象コードを準備

まずはテストしたいコードを作成します。

今回は単純に足し算を行うメソッドと割り算を行うメソッドを持つSampleClassを作成しました。

# sample.py
class SampleClass():
    """テスト対象クラス"""

    def calc_adder(self, num1, num2):
        """引数で受け取った値の和を返却する"""
        return num1 + num2

    def calc_divide(self, num1, num2):
        """引数で受け取った値の商を返却する"""
        return num1 / num2

unittestのテストコードを作成する

上記のSampleClassのテストコードを以下の手順で作成します。

  1. testから始まる名称でテストモジュールを作成する。
  2. テストクラスはunittest.TestCaseクラスを継承する。
  3. testから始まる名称でテストメソッドを作成する。
  4. テストメソッドに実施するテストの処理を記載する。
  5. アサーションメソッドを使用してテスト結果を確認する。

ちなみに、unittestはPython標準の機能であるため、別途インストールは不要です。

# test_sample.py
from unittest import TestCase

from sample import SampleClass


class SampleClassTest(TestCase):
    """SampleClassのテストクラス"""
    def setUp(self):
        self.sample = SampleClass()

    def test_calc_adder(self):
        """calc_adderメソッドのテスト"""
        result = self.sample.calc_adder(num1=10, num2=5)

        self.assertEqual(result, 15)

    def test_calc_divide(self):
        """calc_divideメソッドのテスト"""
        result = self.sample.calc_divide(num1=10, num2=5)

        self.assertEqual(result, 2)

    def tearDown(self):
        del self.sample

「test_sample.py」という名称で上記のテストコードを作成しました。
簡単にテストクラスの説明をします。

  • 1行目:unittest.TestClassクラスを継承するためにTestClassクラスをimport。
  • 3行目:テスト対象クラスのimport。
  • 6行目:TestClassクラスを継承したテストクラスを定義。
  • 11行目~21行目:テストメソッド。テスト対象のメソッドに任意の数値を渡して実行し、想定通り結果が返ってくるかアサーションメソッドを使用して確認する。

ざっくり説明すると上記の通りです。

また、今回の例ではテストモジュールをtestsというパッケージを作成し、その中にテストモジュールを作成しました。
実際の開発では、テストモジュールを複数作ることになると思うので、テストモジュールを格納する専用のパッケージを用意しています。
ディレクトリ構成は以下のようになります。

$ tree /f

.
│ sample.py
│
└─tests
    test_sample.py

setUpメソッド・tearDownメソッド

8行目のsetUpメソッドはテストメソッドが実行される前に必ず実施されるメソッドです。
23行目のtearDownメソッドはテストメソッドの実行後に必ず実施されるメソッドです。

上記のテストコードの実行順序は以下のようになります。

①setUpメソッド → test_calc_adder → tearDownメソッド
②setUpメソッド → test_calc_divide → tearDownメソッド

各テストメソッドで共通の初期処理やテスト対象クラスを実施するための前提条件となる処理等を記載すると良いと思います。
例えば、テスト用のマスタデータなどをテストメソッド実行前にデータベースに登録して、テストメソッド実行後に削除する等といった使い方ができそうですね。

今回の「test_sample.py」では、テストメソッドの実施前にテスト対象クラスのインスタンスを生成し、テストメソッド実行後にインスタンスを破棄するといった用途で使用しています。

unittestを実行する

では実際に上記のテストコードを実行してみます。

unittestの実行は以下のコマンドで行います。

<testsフォルダ内のすべてのテストモジュールを実行する場合>

python -m unittest discover 【テストモジュールのパッケージ名】
例:python -m unittest discover tests

<実行するテストモジュールを指定する場合>

python -m unittest 【パッケージ名】.【テストモジュール名(拡張子を除いたもの)】
例:python -m unittest tests.test_sample

<実行するテストクラスを指定する場合>

python -m unittest 【パッケージ名】.【テストモジュール名(拡張子を除いたもの)】.【テストクラス名】
例:python -m unittest tests.test_sample.SampleClassTest

<実行するテストメソッドを指定する場合>

python -m unittest 【パッケージ名】.【テストモジュール名(拡張子を除いたもの)】.【テストクラス名】.【テストメソッド名】
例:python -m unittest tests.test_sample.SampleClassTest.test_calc_adder

今回はテストモジュールを指定して実行してみます。

$ python -m unittest tests.test_sample
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

「Ran 2 tests」と表示されています。これは2つのテストメソッドが実行されたことを意味します。
そして最後に「OK」と表示されています。実行したテストがすべて成功するとこのように「OK」と表示されます。

では、テストメソッドをもう1つ増やしてみましょう。
追加したテストメソッドでは、割る数(num2)がゼロの場合は、0が返ってくることを想定しています。

    def test_calc_divide_zerodivide(self):
        """calc_divideメソッドの割る数がゼロの場合のテスト"""
        result = self.sample.calc_divide(num1=10, num2=0)

        self.assertEqual(result, 0)

今度は追加したテストメソッドだけを実行してみます。

$ python -m unittest tests.test_sample.SampleClassTest.test_calc_divide_zerodivide
E
======================================================================
ERROR: test_calc_divide_zerodivide (tests.test_sample.SampleClassTest)
calc_divideメソッドのテスト
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\python\demo_unittest\tests\test_sample.py", line 25, in test_calc_divide_zerodivide
result = self.sample.calc_divide(num1=10, num2=0)
File "D:\python\demo_unittest\sample.py", line 10, in calc_divide
return num1 / num2
ZeroDivisionError: division by zero

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (errors=1)

今度は「Ran 1 test」となっており、指定したメソッドのみ実行されたことが確認できます。
また、テスト結果が「FAILED (errors=1)」となっており、テスト対象メソッドでZeroDivisionErrorが発生していることが確認できます。

calc_divideメソッドでは0割の考慮が漏れていたため割る数がゼロの場合はゼロを返却するように修正します。

    def calc_divide(self, num1, num2):
        """引数で受け取った値の商を返却する"""
        if num2 == 0:
            return 0
        else:
            return num1 / num2

そして、改めてunittestを実行してみます。

$ python -m unittest tests.test_sample.SampleClassTest.test_calc_divide_zerodivide
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

成功しました。

ちなみに、先ほどのテスト失敗はテスト対象クラスで想定外の例外エラーが発生した場合でしたが、
実行結果と想定値が違う場合は以下のように「AssertionError」が表示されます。

$ python -m unittest tests.test_sample.SampleClassTest.test_calc_divide
F
======================================================================
FAIL: test_calc_divide (tests.test_sample.SampleClassTest)
calc_divideメソッドのテスト
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\python\demo_unittest\tests\test_sample.py", line 21, in test_calc_divide
self.assertEqual(result, 3)
AssertionError: 2.0 != 3

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)

まとめ

今回はunittestのテストコードの作成方法、実行方法について記載しました。
特に以下の2点についてはしっかりと抑えて、次の機会に活かしたいと思いました。

①テストモジュールは以下の内容で作成する。

  • testから始まる名称でテストモジュールを作成する。
  • テストクラスはunittest.TestCaseクラスを継承する。
  • testから始まる名称でテストメソッドを作成する。

②unittestの実行単位を状況に応じて使い分ける。

参考文献

実際に作業を実施した際には、以下のサイトを参考にさせていただきました。

・公式ドキュメント
https://docs.python.org/ja/3.7/library/unittest.html

・Python標準のunittestの使い方メモ
https://qiita.com/aomidro/items/3e3449fde924893f18ca#references

・Pythonでunittestを実行する(初心者用)
https://qiita.com/takus69/items/cde279266b46daf9972d


unittestの使い方については、他にもいろいろ記載しています。
よろしければ見ていってください。