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


9

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

実証するには:

ここに画像の説明を入力してください

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

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



2
「プライベートメソッドは単体テストに有益です...」「...プライベートメソッドを使用すると、単体テストで有用で信頼性の高い機能が得られます。対照的に、「テスト容易性」のためにアクセス制限を弱めると、あいまいで理解しづらくなるだけです。テストコードの一部。これは、マイナーなリファクタリングによって破損する可能性があり、正直に言えば、私が得たものは技術的な負債のように思われます。」
gnat

3
「プライベート関数をテストする必要がありますか?」いいえ、決して、これまで、決して。
David Arno

2
@DavidArnoなんで?内部をテストする代わりに何がありますか?統合テストのみですか?またはより多くのものを公開していますか?(私の経験では主にプライベートメソッドではなく内部クラスのパブリックメソッドをテストします)
CodesInChaos

1
テストを作成する必要があるほど重要な場合は、すでに別のモジュールにあるはずです。そうでない場合は、パブリックAPIを使用してその動作をテストします。
Vincent Savard、2016年

回答:


14

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

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

これらの競合するビューにより、すべての関数をパブリックにしたり、パブリックでない限り、コードを関数に分解することを拒否したりする可能性があります。

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

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

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

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

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

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

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

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


私はアプローチを思いついたので、あなたの意見を聞きたいと思います。私がプライベート関数をテストしたい状況にいるときはいつでも、パブリック関数の1つを通じて十分にテストできるかどうかを確認します(すべてのエッジケースなどを含む)。できれば、この関数のテストは特に記述しませんが、それを使用するパブリック関数をテストすることによってのみテストします。ただし、関数がパブリックインターフェイスを介して十分にテストできず、独自のテストに値すると感じた場合は、その関数をパブリックモジュールの内部モジュールに抽出し、テストを記述します。どう思いますか?
Aviv Cohn

私はここで答えた他の人たちと同じことを尋ねていると言います:)私はみんなの意見を聞きたいです。
Aviv Cohn

繰り返しますが、私はあなたにノーとは言いません。それがユーザビリティにどのように影響するかに注意を払うことについて何も言わなかったことを心配しています。パブリックとプライベートの違いは構造的なものではありません。使い方です。パブリックとプライベートの違いがフロントドアとバックドアである場合、回避策は裏庭に小屋を建てることです。いいね。人々がそこで迷子にならない限り。
candied_orange 2016

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

7

短い答え:いいえ

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

クラスのプライベートメンバーの全体的な考え方は、コードの「ユニット」の外では見えないはずの機能を表すということですが、そのユニットをどのくらい大きく定義したいかということです。オブジェクト指向コードでは、そのユニットはしばしばクラスになります。

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


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


こんにちは。回答ありがとうございます。質問をもう一度読んでください。明確にするために編集しました。
Aviv Cohn 2016年

私はアプローチを思いついたので、あなたの意見を聞きたいと思います。私がプライベート関数をテストしたい状況にいるときはいつでも、パブリック関数の1つを通じて十分にテストできるかどうかを確認します(すべてのエッジケースなどを含む)。できれば、この関数のテストは特に記述しませんが、それを使用するパブリック関数をテストすることによってのみテストします。ただし、関数がパブリックインターフェイスを介して十分にテストできず、独自のテストに値すると感じた場合は、その関数をパブリックモジュールの内部モジュールに抽出し、テストを記述します。どう思いますか?
Aviv Cohn

私はここで答えた他の人たちと同じことを尋ねていると言います:)私はみんなの意見を聞きたいです。
Aviv Cohn

5

プライベート関数の要点は、パブリック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の変更。テストと密接に関連する露出された内部の仕組み。実装に変更が加えられるとすぐに変更される脆弱なテスト。

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


こんにちはデビッド、あなたの答えをありがとう。私の質問をもう一度読んでください。明確にするために編集しました。
Aviv Cohn 2016年

ええ。内部関数のテストに問題はありません。個人的には、いくつかの単体テストでカバーできない限り、どんな種類の重要な関数も記述しません。そのため、内部関数のテストを禁止すると、内部関数を記述できなくなり、非常に便利なテクニックが奪われます。APIは変更できます。その場合は、テストを変更する必要があります。内部関数(およびそのテスト)をリファクタリングしても、外部関数のテストは中断されません。それがそれらのテストをすることの要点です。全体的に悪いアドバイス。
Robert Harvey

あなたが本当に主張しているのは、プライベート関数を持ってはいけないということです。
Robert Harvey

1
@AvivCohnそれらはテストを保証するのに十分な大きさであるか、その場合、それらは独自のファイルを取得するのに十分な大きさです。または、それらを個別にテストする必要がないほど小さい。どっち?
Doval

3
@RobertHarveyいいえ、それは、「必要に応じて大きなクラスを疎結合コンポーネントに分解する」という議論を作ります。それらが本当に公開されていない場合は、おそらくパッケージプライベートの可視性の良いユースケースです。
Doval
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.