web-dev-qa-db-ja.com

プライベート関数は、単にまだ抽出されていない機能の内部ユニットである場合があります。では、なぜそれらをテストしないのですか?

モジュールまたはクラスのプライベート関数は、まだ抽出されていない機能の内部ユニットである場合があり、独自のテストに値する場合があります。では、なぜそれらをテストしないのですか?私たちは後でそれらが抽出された場合/時にそれらのテストを記述します。では、同じファイルにまだ含まれているテストを書いてみませんか?

実証するには:

enter image description here

まず、module_a。それをテストしたいのですが。 「プライベート」関数をテストしたい_private_func。なぜそれをテストするのかわからないのですが、後でそれを独自の内部モジュールにリファクタリングする可能性がある場合は、、次に書き込みますそれをテストします。


次の関数を持つモジュールがあると仮定します(クラスの場合もあります)。

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

_do_stuffおよび_do_more_stuffは、モジュールの「プライベート」関数です。

実装の詳細ではなく、パブリックインターフェイスのみをテストする必要があるという考えを理解しています。ただし、これは次のとおりです。

_do_stuffおよび_do_more_stuffモジュールの機能の大部分が含まれています。それらのそれぞれは、異なる「内部」モジュールのパブリック関数である可能性があります。しかし、それらはまだ進化しておらず、個別のファイルに抽出するのに十分な大きさではありません。

したがって、これらの関数は機能の重要な単位であるため、これらの関数のテストは適切だと感じます。それらがパブリック関数として異なるモジュールにあった場合、それらをテストしたでしょう。それで、それらがまだ(またはこれまでに)別のファイルに抽出されていないときにテストしてみませんか?

9
Aviv Cohn

テストする必要性は公開する必要性と同じではありません。

重要なコードは、公開に関係なくテストが必要です。非公開の振る舞いは、テストされることはもちろん存在する必要もありません。

これらの矛盾するビューにより、すべての関数をパブリックにしたい場合や、パブリックでない限り、コードを関数に分解しない場合があります。

これは答えではありません。プライベートヘルパー関数を作成することをいとわない。可能な限りそれを使用する公開インターフェースを介してそれらをテストします。

パブリックインターフェイスを介したテストでプライベート関数が十分に機能しない場合、プライベート関数は多くのことを許可しようとしています。

検証は、プライベート関数が許可するものを絞り込むのに役立ちます。パブリックインターフェイスを通過するnullを渡すことができない場合でも、とにかく通過した場合は例外をスローできます。

なぜあなたは?決して表示されないものをテストするのはなぜですか?物事が変わるからです。現在は非公開ですが、後で公開される可能性があります。呼び出しコードは変更される可能性があります。 nullを明示的に拒否するコードは、適切な使用法と予期される状態を明確にします。

もちろんnullでもかまいません。これはほんの一例です。しかし、あなたが何かを期待するなら、それはその期待を明確にするのに役立ちます。

それはあなたが考えていた種類のテストではないかもしれませんが、うまくいけば、適切なときにプライベートヘルパー関数を作成しても構わないと思います。

テストしたいという願望は良いですが、それはあなたの公開APIの設計の原動力であってはなりません。使いやすいようにパブリックAPIを設計します。すべての関数が公開されている場合はそうではないでしょう。 APIは、コードに飛び込むことなく使用方法を理解できるものでなければなりません。このような奇妙なヘルパー機能が何のためにあるのかと不思議に思う人を放置しないでください。

内部モジュールでパブリックヘルパー関数を非表示にすることは、テスト用にヘルパーを公開する一方で、クリーンなAPIの必要性を尊重する試みです。これが間違っているとは言いません。別のアーキテクチャー層に向けた最初のステップを踏んでいる可能性があります。ただし、プライベートヘルパー関数を最初に使用するパブリック関数を介してテストする方法を習得してください。そうすれば、この回避策を使い過ぎることはありません。

14
candied_orange

短い答え:いいえ

より長い回答:はい、ただしクラスのパブリック「API」を介して

クラスのプライベートメンバーの全体的な考え方は、コードの「ユニット」の外では見えないはずの機能を表すということですが、そのユニットをどのように定義する必要があるかは重要です。オブジェクト指向コードでは、多くの場合、その単位はクラスになります。

入力状態のさまざまな組み合わせを通じてすべてのプライベート機能を呼び出すことができるようにクラスを設計する必要があります。これを行うための比較的簡単な方法がない場合は、おそらくデザインにもっと注意が必要であることを示唆しています。


質問を明確にした後、これは単なる意味論の問題です。問題のコードが独立したスタンドアロンユニットとして動作し、パブリックコードであるかのようにテストされている場合、スタンドアロンモジュールに移動しないことの利点はわかりません。現時点では、明らかにパブリックコードが別のモジュール内に隠されている理由について、将来の開発者(自分も含めて6か月後)を混乱させるだけです。

7
richzilla

プライベート関数の要点は、パブリックAPIを変更せずに、自由に変更できる隠された実装の詳細であることです。あなたのサンプルコードについて:

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

public_funcのみを使用する一連のテストがある場合、次のように書き直します。

def public_func(a):
    b = _do_all_the_new_stuff(a)
    return _do_all_the_old_stuff(b)

次に、特定の値aの戻り結果が同じである限り、すべてのテストが適切になります。返される結果が変わると、テストは失敗し、何かが壊れたという事実が明らかになります。

これはすべて良いことです。静的なパブリックAPI。カプセル化された内部の仕組み。堅牢なテスト。

ただし、_do_stuffまたは_do_more_stuffのテストを記述してから上記の変更を行った場合、機能が変更されたためではなく、その実装のために、一連の壊れたテストが発生します。機能が変更されました。これらのテストは新しい関数で動作するように書き換える必要がありますが、それらを動作させると、これらのテストが新しい関数で動作したことだけがわかります。あなたは元のテストを失っていたので、public_funcの動作が変更されたかどうかわからないので、テストは基本的に役に立たないでしょう。

これは悪いことです。APIの変更。テストと密接に結びついた露出した内部の仕組み。実装に変更が加えられるとすぐに変化する脆弱なテスト。

したがって、プライベート関数をテストしないでください。ずっと。

5
David Arno