Pythonパッケージのテストを書く#

パッケージのコードをテストするコードを書くことは、テスト・スイートとしても知られています。 テスト・スイートは、関数、メソッド、クラスのセットで構成され、コードの特定の部分が期待通りに動作することを確認することを意図して書かれます。

なぜパッケージのテストを書くのか?#

テストはコード変更のセーフティネットとして機能します。 バグがユーザーに影響を与える前に発見し、修正するのに役立ちます。 テストはまた、貢献者によるコード改変が既存の機能を壊さないという確信を与えます。

Pythonパッケージのテストを書くことは重要です:

  • キャッチミス: テストはセーフティネットです。パッケージに変更を加えたり、新機能を追加したりした場合、テストによって、以前は問題なく動作していたものを誤って壊してしまったかどうかをすぐに知ることができます。

  • 時間を節約する: あなたのパッケージがまだ正常に動作しているかどうかを自動的にチェックできる魔法のボタンがあるとしましょう。テストは魔法のボタンのようなものです! あなたの時間を節約するために、すべてのチェックを実行してくれます。

  • より容易なコラボレーション: 他の人と一緒に仕事をしていたり、外部の協力者がいる場合、テストは全員が同じページにとどまるのに役立ちます。テストは、パッケージがどのように動作するかを説明し、他の人が理解しやすくし、プロジェクトに貢献しやすくします。

  • 大胆不敵なリファクタリング: リファクタリングとは、コードの振る舞いを変えることなく、コード構造を改善することです。 テストは、あなたが何かを壊した場合、テストの失敗があなたに知らせてくれるので、このような変更を行う力を与えてくれます。

  • 文書化: テストは、あなたのパッケージの使い方の技術的な例となります。 これは、あなたのパッケージにコードを提供しようとする新しい技術的貢献者にとって役に立ちます。 彼らはあなたのテストを見て、コードの機能の一部がどのように組み合わされているかを理解することができます。

  • 長期的なメンテナンスの容易さ: パッケージが進化するにつれて、テストは、時間の経過とともに変更を加えても、コードが期待通りに動作し続けることを保証します。こうして、テストを書くときに将来の自分を助けることになります。

  • Easier pull request reviews: By running your tests in a CI framework such as GitHub Actions, each time you or a contributor makes a change to your code-base, you can catch issues and things that may have changed in your code base. This ensures that your software behaves the way you expect it to.

ユーザーエッジケースのテスト#

エッジケースとは、あるユーザがあなたのパッケージを使用する際の、予期せぬ、あるいは "外れ値" の使い方を指します。 テストによって、パッケージの機能を損なう可能性のある様々なエッジケースに対処することができます。 例えば、ある関数がpandasの dataframe を期待したのに、ユーザがnumpyの array を提供した場合、何が起こるでしょうか? あなたのコードは、このような状況を丁寧に処理し、明確なフィードバックを提供していますか?それとも、原因不明の失敗でユーザーをイライラさせたままにしていますか?

注釈

テストの入門書としては、 このSoftware Carpentryのレッスン を参照してください。

../_images/python-tests-puzzle.png

パズルのピースが、あなたのPythonパッケージの関数、メソッド、クラス、属性を表しているとしましょう。 そしてあなたは他の人に使ってもらいたいと思っています。 ピースが欠けていたり、ピースが合わなかったりするパズルを誰かに渡したいと思いますか? パズルのピースを正しく組み合わせて提供することは、Python パッケージのテストを書くことに例えることができます。#

テスト例

例えば、2つの数値aとbを足し合わせるPython関数があるとしましょう。

def add_numbers(a, b):
    return a + b

異なる数値が与えられたときに、その関数が期待通りに実行されることを確認するテストは次のようになります:

def test_add_numbers():
    result = add_numbers(2, 3)
    assert result == 5, f"Expected 5, but got {result}"

    result2 = add_numbers(-1, 4)
    assert result2 == 3, f"Expected 3, but got {result2}"

    result3 = add_numbers(0, 0)
    assert result3 == 0, f"Expected 0, but got {result3}"

test_add_numbers()

🧩🐍

どのような種類のテストを書けばいいのか、どうすればわかりますか?#

注釈

このセクションは、 Nick Murphyのプレゼンテーション から引用しました。

この時点で、あなたは疑問に思うかもしれません-あなたのパッケージで何をテストすべきか? 以下にいくつか例を挙げます:

  • いくつかの典型的なケースをテスト: パッケージが、ユーザーが使用したときに期待通りに機能することをテストします。例えば、パッケージが2つの数値を足すことになっている場合、その2つの数値を足した結果が正しいかどうかをテストします。

  • 特別なケースをテストする: 時には特殊なケースや異常なケースもあります。例えば、ある関数が特定の計算を実行し、その計算が値=0に近いほど問題になる可能性がある場合、その関数の入力を0と

  • 予想される境界線とその付近でテストする: 関数が1以上の値を必要とする場合、この関数が、1と1未満と1.001の両方の値 (制約値に近い値) でも動作することを確認してください。

  • コードが正しく失敗するかテストする 関数が1以上の値を必要とする場合は、0.999でテストします。 予期しない値やヘルプが与えられたときに、関数が差し障り無く失敗し、ユーザが失敗した理由を簡単に理解できるようにします(有用なエラーメッセージを提供します)。