Python でのパラメーター化された単体テスト

Abid Ullah 2023年6月21日
  1. Python でのパラメーター化された単体テストの目的
  2. Python でのパラメーター化された単体テストの例
  3. Python でのパラメーター化された単体テストの利点
  4. パラメータ化された単体テストをサポートする Python ライブラリ
Python でのパラメーター化された単体テスト

単体テストは、ソフトウェアの品質を維持するための強力なツールの 1つです。 ソフトウェアの単体テストは、ソフトウェアがソフトウェア設計仕様に沿って開発され、エンドユーザーに対して意図したとおりに動作または応答することを確認するための、コードの一部に対する一連のチェックです。

Python では、パラメーター化された単体テストを使用して、外出先で項目またはテスト ケースごとに 1つのテストを生成できます。 この記事では、Python のパラメーター化された単体テストについて説明します。

Python でのパラメーター化された単体テストの目的

開発者が単体テストを作成する場合、多くの場合、1つのケースに対して 1つのテストというアプローチを採用します。 次に、最も類似または関連するテストがスイートに結合されます。

以下のコードの一連のテストを見てください。

class TestSuite(TestCase):
    def test_first_case(self):
        assert example_test()

    def test_second_case(self):
        assert more_example_tests()

    def test_third_case(self):
        assert another_example_test()

このようなスイートによるテストは、各テスト ケースが、準備、実行、そして最後にアサーションという一連のカスタム ステージを経る必要があることを意味します。 これらのテスト ケースのそれぞれが類似している場合、結果として得られるスイートは重複または冗長なコードの混乱になり、ソフトウェア開発者の世界では呪いにほかなりません。

これらのスイートはまた、変更が行われたときに維持するのが非常に困難な大規模なコード ベースをもたらします (これは予想されることです)。 この問題に対処するために、Python で動的またはパラメーター化された単体テストを利用できるようになり、複数のケースに対して単一のテストを生成できるようになりました。

Python でのパラメーター化された単体テストの例

Python でのパラメーター化された単体テストを理解するためのベースとして使用できるテスト シナリオまたはメソッドの例と、ソフトウェア開発者としての作業がどのように容易になるかについて詳しく見ていきましょう。

以下は、テストしたい単純な関数です。

def compute(a, b):
    return (a + b) / (a * b)

これで、他のテストと同様に、目的の方法で動作することをテストするために上記の関数に適用する必要がある入力が複数あることがわかりました。 テスト パラメータをより精巧に指定するには、次のパラメータに対してメソッドをテストする必要があります。

  • 正の整数
  • 負の整数
  • 長整数
  • 通常の整数
  • 浮動小数点数
  • いずれかまたは両方の数値がゼロ
  • 引数のいずれかまたは両方が未定義
  • いずれかまたは両方の引数が文字列またはその他の種類のオブジェクトである

ケースごとに 1つのテスト方法を使用してこれをテストする場合、1 行の方法に対して長い一連の繰り返しコードを作成することになります。 ループや条件を含む複数行のコードに基づくメソッドの場合、これがどれほど多忙で退屈になるか想像できます。

パラメータ化では、これを行うだけで済みます。

def test_compute(self, a, b, expected, raises=None):
    if raises is not None:
        with self.assertRaises(raises):
            compute(a, b)
    else:
        assert compute(a, b) == expected

これにより、compute メソッドのすべての可能性を最適にテストするパラメーターのリストを準備する必要があります。 パラメータのリストは、必要なだけ長くすることも、テストする必要があるケースのコレクションにすることもできます。

上記の単体テストに渡すことができるパラメーターのリストの例を次に示します。

コードの出力:

a           | b   | expected | raises
------------+-----+----------+-------
-2          | 2   | 0        |
0           | 2   |          | ZeroDivisionError
2           | 2   | 1        |
0.5         | 0.4 | 4.5      |
None        | 2   |          | TypeError
"10"        | "1" |          | TypeError
3000000000L | 2   | 0.5      |

おわかりのように、このリストと上記のパラメーター化された単体テストにより、compute() メソッドに渡すことができる引数のさまざまなシナリオをテストすることがはるかに効率的になります。 また、さまざまな結果の記録がはるかに簡単になります。

Python でのパラメーター化された単体テストの利点

定期的な単体テストの作成は、非常に反復的で、網羅的で、時間がかかり、神経をすり減らす可能性があります。特に、テストで各ソフトウェア シナリオを実行し、すべてが目標どおりであることを確認する必要がある場合はなおさらです。

Python でパラメーター化された単体テストを使用すると、開発者は、開発中のソフトウェアの品質を維持するために発生する可能性のあるさまざまなシナリオをテストするために、何百ものコード行を記述することを心配する必要がなくなります。 必要なのは、テストが必要なすべてのシナリオをテストする単一のテストだけです。

これは、テスト コード ベースが常に簡単に維持でき、繰り返しがほとんどまたはまったくないことを意味し、その結果、効率的なテスト ケースと時間を節約できる結果が得られます。

さらに、パラメーター化された単体テストにより、問題の開発者は、メソッドに入力される可能性のあるすべての入力について、より明確かつ明確に考える必要があります。 これは、上記の例の大きな整数や一貫性のない入力タイプなどのエッジ ケースや、未定義の入力などの誤ったケースを追跡するのに役立ちます。

また、このテストでは、開発者は、コードを壊す可能性のある入力の種類について考えるだけでなく、エッジ ケースをどのように処理できるかについても考える必要があります。 特定の状況の組み合わせの下で関数が何をすることが期待されるかを予測することを強制することで、コードの品質を維持するのに役立ち、直面する問題に対する概念的かつ効率的な解決策の開発につながります。

それに加えて、メソッドをテストするために、より網羅的または広範なパラメーターのリストを使用することを決定した場合、テスト ケースを作成するにはあまりにも単純すぎると最初は考えていたテスト ケースの結果と解決策が得られる可能性があります。 ただし、パラメータ化されたテストを自由に使用できるため、テストしたい組み合わせを入力するだけで、コードがどのように応答するかを確認できます。

パラメータ化された単体テストをサポートする Python ライブラリ

Python は、テスト メソッドの開発を簡単にする parameterized ライブラリを使用して、私たちの生活を楽にしてくれます。 parameterized をインポートし、次のようにテスト スイートのメソッドで parameterized デコレーターを介してテストするパラメーターを渡すだけです。

from parameterized import parameterized


class TestSuite(TestCase):
    @parameterized.expand([("test_1", 0, 0, 0), ("test_2", 0, 1, 1)])
    def test_my_feature(self, name, in_1, in_2, expected):
        assert my_feature(in_1, in_2) == expected

これを行う別の方法は、すべてのパラメーターのリストを含む .csv ファイルを作成することです。 次に、それをデコレータに渡して次のようにテストします。

def load_test_cases():
    return load_from_csv("my_feature_parameters.csv")


class TestSuite(TestCase):
    @parameterized.expand(load_test_cases)
    def test_my_feature(self, name, in_1, in_2, expected):
        assert my_feature(in_1, in_2) == expected

動的 (パラメーター化された) 単体テストを使用する必要がある理由から、それを実装するさまざまな方法と、ソフトウェア開発ライフサイクルにおけるその利点まで、すべてをカバーしました。 この記事が、Python で単体テストを生成する方法を理解するのに役立つことを願っています。

著者: Abid Ullah
Abid Ullah avatar Abid Ullah avatar

My name is Abid Ullah, and I am a software engineer. I love writing articles on programming, and my favorite topics are Python, PHP, JavaScript, and Linux. I tend to provide solutions to people in programming problems through my articles. I believe that I can bring a lot to you with my skills, experience, and qualification in technical writing.

LinkedIn

関連記事 - Python Unit Test