web-dev-qa-db-ja.com

ユニットテストで重複したコードは許容できますか?

いくつかの単体テストを少し前に台無しにして、それらをリファクタリングしてそれらをさらに作成しました [〜#〜] dry [〜#〜] -各テストの意図はもはや明確ではありませんでした。テストの可読性と保守性の間にはトレードオフがあるようです。ユニットテストで重複したコードを残すと、読みやすくなりますが、 [〜#〜] sut [〜#〜] を変更すると、それぞれを追跡して変更する必要があります複製されたコードのコピー。

このトレードオフが存在することに同意しますか?もしそうなら、あなたはあなたのテストを読みやすくするか、維持することを好みますか?

110
Daryl Spitzer

重複したコードは、他のコードと同様に単体テストコードのにおいです。テストでコードを複製した場合は、更新するテストの数が極端に増えるため、実装コードのリファクタリングが難しくなります。テストは、テスト対象のコードでの作業を妨げる大きな負担になるのではなく、自信を持ってリファクタリングするのに役立ちます。

複製がフィクスチャの設定にある場合は、setUpメソッドをより活用するか、より多くの(またはより柔軟な) 作成方法 を提供することを検討してください。

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

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

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

次に、assertPersonEqual(Person('Joe', 'Bloggs', 23), person)を記述できるように、おそらく単一のassertPersonEqualメソッドが必要になります。 (あるいは、Personの等値演算子をオーバーロードする必要があるだけかもしれません。)

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

とはいえ、テストは、類似しているものの大幅に異なる複雑な状況を実行している場合があり、重複を減らすための良い方法を見つけるのは困難です。常識を使用します。テストが読みやすく、意図が明確で、テストによって呼び出されたコードをリファクタリングするときに、理論的に最小限の数を超えるテストを更新する必要がある場合は、問題を受け入れて移動します。より生産的なものに移ります。いつでも戻って、後からテストをリファクタリングして、インスピレーションが得られます。

68
spiv

テストでは読みやすさが重要です。テストが失敗した場合は、問題を明確にする必要があります。何が失敗したかを正確に特定するために、開発者は多くの厳密に因数分解されたテストコードをたどる必要はありません。ユニットテストテストを書く必要があるほどテストコードが複雑になりたくない。

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

178

実装コードとテストは異なる動物であり、ファクタリングルールはそれらに異なる方法で適用されます。

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

一方、テストコードでは、ある程度の重複を維持する必要があります。テストコードの複製により、次の2つの目標が達成されます。

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

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

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

テスト方法が長くて繰り返しの多いものになると、いくつかのパラメーターを取る空欄埋めテストテンプレートを定義すると便利な場合があります。次に、実際のテストメソッドは、適切なパラメーターを使用したテンプレートメソッドの呼び出しに削減されます。

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

44
ddaa

test utility methods のいくつかの異なるフレーバーを使用して、繰り返しを減らすことができます。

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

8
Don Kirkby

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

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

7
stucampbell

Jay Fieldsは、「DSLはDRYではなくDAMPである必要がある」というフレーズを作り出しました。ここで、[〜#〜] damp [〜#〜]説明的で意味のあるフレーズを意味します。同じことがテストにも当てはまると思います。明らかに、あまりにも多くの複製は悪いです。しかし、すべてのコストで重複を取り除くことはさらに悪いことです。テストは、意図を明らかにする仕様として機能する必要があります。たとえば、同じフィーチャを複数の異なる角度から指定した場合、ある程度の重複が予想されます。

6
Jörg W Mittag

テストコードには、通常は量産コードに適用されるのと同じレベルのエンジニアリングが必要だと思います。読みやすさを優先して行われた議論は確かにあり得、私はそれが重要であることに同意します。

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

テストが困難な場合に適切な抽象化レベルを見つけることは難しく、実行する価値があると感じています。

3
Kevin London

このため、rspecが大好きです。

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

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

  • ネストされたコンテキスト。
    基本的に、クラスのすべてのテストだけでなく、テストの特定のサブセットに対して「セットアップ」および「ティアダウン」メソッドを使用できます。

.NET/Java /その他のテストフレームワークがこれらのメソッドを採用するのが早ければ早いほどよい(または、IronRubyまたはJRubyを使用してテストを作成することができます。

3
Orion Edwards

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

リファクタリングの実行に問題があったようです。私は推測しているだけですが、それが明らかに不明確になったとしても、完全に明確である適度にエレガントなテストを行うために、まだやらなければならないことがまだないのですか?

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

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

あなたは-Python、Java、C#-を使っているどんな言語の能力も持っているので、その言語を上手に使ってください。明確で冗長すぎない見栄えの良いテストコードを実現できます。トレードオフはありません。

2
S.Lott

重複したコードと読みやすいコードの間に関係があるとは思いません。あなたのテストコードは他のコードと同じくらい良いはずです。繰り返しを行わないコードは、適切に実行すると、重複したコードよりも読みやすくなります。

2
Paco

理想的には、一度記述された単体テストはそれほど変更されないので、読みやすさを重視します。

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

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

2
17 of 26