127

少し前にいくつかの単体テストを台無しにして、それらをよりDRYにするためにリファクタリングしました。各テストの意図はもはや明確ではありませんでした。テストの可読性と保守性の間にはトレードオフがあるようです。重複したコードを単体テストに残しておくと、読みやすくなりますが、SUTを変更すると、重複したコードの各コピーを追跡して変更する必要があります。

このトレードオフが存在することに同意しますか? もしそうなら、あなたのテストが読みやすいものであること、または保守可能であることのどちらを好みますか?

4

11 に答える 11

214

テストでは読みやすさがより重要です。テストが失敗した場合、問題を明らかにする必要があります。開発者は、何が失敗したかを正確に判断するために、多くの因数分解された多くのテスト コードを調べる必要はありません。単体テスト テストを記述する必要があるほどテスト コードが複雑になることは望ましくありません。

ただし、重複を排除することは通常、何も不明瞭にならない限り良いことであり、テストで重複を排除することはより良い API につながる可能性があります。収益が減少するポイントを超えないように注意してください。

于 2008-09-24T20:25:01.700 に答える
72

重複したコードは、他のコードと同じように、単体テスト コードの臭いです。テストに重複したコードがあると、更新するテストの数が不均衡になるため、実装コードのリファクタリングが難しくなります。テストは、テスト対象のコードでの作業を妨げる大きな負担になるのではなく、自信を持ってリファクタリングするのに役立つはずです。

複製がフィクスチャのセットアップにある場合は、setUpメソッドをさらに活用するか、より多くの (またはより柔軟な) Creation Methodsを提供することを検討してください。

SUT を操作するコードに重複がある場合は、複数のいわゆる「ユニット」テストがまったく同じ機能を実行している理由を自問してください。

重複がアサーションにある場合は、おそらくいくつかのカスタム アサーションが必要です。たとえば、複数のテストに次のようなアサーションの文字列がある場合:

assertEqual('Joe', person.getFirstName())
assertEqual('Bloggs', person.getLastName())
assertEqual(23, person.getAge())

assertPersonEqual次に、おそらく、単一のメソッドが必要になるため、 assertPersonEqual(Person('Joe', 'Bloggs', 23), person). (または、単純に等値演算子をオーバーロードする必要があるかもしれませんPerson。)

おっしゃる通り、テストコードは読みやすいことが重要です。特に、テストの意図が明確であることが重要です。多くのテストがほとんど同じように見える場合 (たとえば、行の 4 分の 3 が同じまたは実質的に同じ)、それらを注意深く読んで比較しないと、大きな違いを見つけて認識するのは困難です。したがって、すべてのテストメソッドのすべての行がテストの目的に直接関連しているため、重複を削除するためのリファクタリングが読みやすさに役立つことがわかりました。これは、直接関連する行をランダムに組み合わせたり、単なるボイラープレートの行よりも、読者にとってはるかに役立ちます。

とはいえ、テストでは、類似しているが大きく異なる複雑な状況が実行される場合があり、重複を減らす良い方法を見つけるのは困難です。常識を働かせてください: テストが読みやすく、その意図が明確であると感じ、テストによって呼び出されたコードをリファクタリングするときに、理論的に最小限の数以上のテストを更新する必要があることに満足している場合は、不完全さを受け入れて移動してください。より生産的なものに。インスピレーションが湧いたら、いつでも戻ってテストをリファクタリングできます。

于 2008-09-27T14:26:56.707 に答える
54

実装コードとテストは異なる動物であり、因数分解ルールはそれらに異なる方法で適用されます。

重複したコードまたは構造は、実装コードでは常ににおいがします。ボイラープレートを実装し始めたら、抽象化を修正する必要があります。

一方、テストコードは重複のレベルを維持する必要があります。テストコードの複製は、次の2つの目標を達成します。

  • テストを切り離しておく。過度のテスト結合は、コントラクトが変更されたために更新が必要な単一の失敗したテストを変更することを困難にする可能性があります。
  • テストを分離して意味のあるものに保つ。単一のテストが失敗した場合、それが何をテストしているかを正確に見つけることは合理的に簡単でなければなりません。

各テストメソッドが約20行より短い限り、テストコードの些細な重複を無視する傾向があります。セットアップ-実行-検証のリズムがテストメソッドで明らかになるのが好きです。

テストの「検証」部分で重複が発生する場合、カスタムアサーションメソッドを定義すると便利なことがよくあります。もちろん、これらのメソッドは、メソッド名で明らかにできる明確に識別された関係をテストする必要があります:assertPegFitsInHole->良い、assertPegIsGood->悪い。

テストメソッドが長く反復的になると、いくつかのパラメータを使用する穴埋めテストテンプレートを定義すると便利な場合があります。次に、実際のテストメソッドは、適切なパラメーターを使用したテンプレートメソッドの呼び出しに縮小されます。

プログラミングとテストの多くのことに関して、明確な答えはありません。あなたは味を開発する必要があり、そうするための最良の方法は間違いを犯すことです。

于 2008-09-24T21:17:11.980 に答える
8

テストユーティリティメソッドのいくつかの異なるフレーバーを使用して、繰り返しを減らすことができます。

私は本番コードよりもテストコードの方が繰り返しに寛容ですが、時々イライラします。クラスの設計を変更し、戻って同じセットアップ手順を実行する10の異なるテストメソッドを微調整する必要がある場合、イライラします。

于 2008-09-24T21:17:47.313 に答える
8

同意します。トレードオフは存在しますが、場所によって異なります。

状態を設定するために重複したコードをリファクタリングする可能性が高くなります。ただし、実際にコードを実行するテストの部分をリファクタリングする可能性は低くなります。とはいえ、コードを実行するのに常に数行のコードが必要な場合は、それは匂いだと思い、テスト対象の実際のコードをリファクタリングするかもしれません。これにより、コードとテストの両方の可読性と保守性が向上します。

于 2008-09-24T20:30:07.977 に答える
7

Jay Fields は、「DSL は DRY ではなく DAMP であるべきだ」というフレーズを作り出しました。ここで、DAMP説明的で意味のあるフレーズを意味します。テストも同じだと思います。明らかに、重複が多すぎるのは良くありません。しかし、どんな犠牲を払っても重複を取り除くのはさらに悪いことです。テストは、意図を明らかにする仕様として機能する必要があります。たとえば、同じ機能をいくつかの異なる角度から指定すると、ある程度の重複が予想されます。

于 2008-09-28T03:48:28.853 に答える
3

「それらをよりドライにするためにリファクタリングしました-各テストの意図はもはや明確ではありませんでした」

リファクタリングに問題があったようです。推測しているだけですが、結果がはっきりしなくなったとしても、完全に明確な適度にエレガントなテストを行うために、まだやるべきことがたくさんあるということではありませんか?

これが、テストがUnitTestのサブクラスである理由です。そのため、正しく、検証が容易で、クリアな優れたテストスイートを設計できます。

昔は、さまざまなプログラミング言語を使用したテストツールがありました。快適で作業しやすいテストを設計することは困難(または不可能)でした。

使用している言語が何であれ、Python、Java、C#のすべての機能を利用できるので、その言語を上手に使用してください。明確で冗長すぎない見栄えの良いテストコードを実現できます。トレードオフはありません。

于 2008-09-25T00:03:29.163 に答える
3

私はこれのためにrspecが大好きです:

役立つことが2つあります-

  • 一般的な動作をテストするための共有サンプルグループ。
    テストのセットを定義してから、そのセットを実際のテストに「含める」ことができます。

  • ネストされたコンテキスト。
    基本的に、クラス内のすべてのテストだけでなく、テストの特定のサブセットに対して「setup」メソッドと「teardown」メソッドを使用できます。

.NET / Java /その他のテストフレームワークがこれらのメソッドを採用するのが早ければ早いほど良いです(または、IronRubyまたはJRubyを使用してテストを作成できます。これは、個人的にはより良いオプションだと思います)。

于 2008-09-25T00:11:02.717 に答える
3

テスト コードには、通常は製品コードに適用されるのと同様のレベルのエンジニアリングが必要だと思います。読みやすさを優先する議論が確かに存在する可能性があり、それが重要であることに同意します。

しかし、私の経験では、十分に因子分解されたテストの方が読みやすく、理解しやすいことがわかっています。変更された 1 つの変数と最後のアサーションを除いて、それぞれが同じように見える 5 つのテストがある場合、その 1 つの異なる項目が何であるかを見つけるのは非常に困難になる可能性があります。同様に、変化している変数とアサーションのみが表示されるように因数分解されている場合、テストが何を行っているかをすぐに把握するのは簡単です。

テスト時に適切な抽象化レベルを見つけるのは難しい場合がありますが、それを行う価値があると感じています。

于 2015-05-02T22:16:56.767 に答える
2

理想的には、単体テストは一度書かれた後はあまり変更されるべきではないので、読みやすさに傾倒します。

単体テストを可能な限り個別にすることは、テストが対象とする特定の機能に焦点を合わせ続けるのにも役立ちます。

そうは言っても、一連のテストでまったく同じセットアップコードなど、何度も使用することになるコードの特定の部分を再利用しようとする傾向があります。

于 2008-09-24T20:29:04.843 に答える
2

より複製されたコードと読みやすいコードの間に関係があるとは思いません。あなたのテスト コードは、他のコードと同じくらい良いものであるべきだと思います。繰り返しのないコードは、適切に作成された場合、複製されたコードよりも読みやすくなります。

于 2008-09-24T20:26:47.483 に答える