コピーアンドペーストされたテストコード:これはどれほど悪いですか?


12

私の現在の仕事は、主に私たちが取り組んでいるさまざまなアプリケーションのGUIテストコードを書くことです。ただし、テスト内で多くのコードをコピーして貼り付ける傾向があることがわかりました。この理由は、テストしている領域は、繰り返しが必要なほど似ている傾向があるが、コードをメソッドまたはオブジェクトにカプセル化するほど似てない傾向があるためです。クラスやメソッドをより広範囲に使用しようとすると、テストの保守が面倒になり、最初から書くのが完全に難しくなることがあります。

代わりに、通常、あるセクションからテストコードの大きな部分をコピーして別のセクションに貼り付け、必要な小さな変更を加えます。オブジェクト指向の原則や関数を使用するなど、構造化されたコーディング方法は使用しません。

テストコードを書くとき、他のコーダーはこのように感じますか?DRYとYAGNIの原則に従うことは明らかですが、テストコード(とにかくGUIテスト用の自動化されたテストコード)を使用すると、これらの原則に従うことが難しくなることがわかります。または、より多くのコーディングの練習とより良い全体的なシステムが必要ですか?

編集:私が使用しているツールはSilkTestで、4Testと呼ばれる独自の言語です。同様に、これらのテストは主にWindowsデスクトップアプリケーション用ですが、このセットアップを使用してWebアプリもテストしました。


どのテストツールを使用していますか?テストフレームワークが、作成しているタイプのテストをサポートしていない可能性があります。3行以上のカットアンドペーストは一般的に非常に悪いですが、毎回手動で実行するよりもGUIテストを自動化することでより長期的な価値を明確に追加できる場合は、おそらくあなたがしていることは何でもないことです良い。
グレンペターソン

また、これは何語ですか?あなたは可能(第一級関数など)の再利用が可能になるだけで心に飛び出るていない利用可能なものを、持っています。一方、テストケース単純であり、バグが発生する可能性を低くするために
...-Izkata

3
...何で私は、書いたコードをテストすることはリファクタリングから除外されていません
サイモン・ホワイトヘッド

回答:


23

多くの場合コピーして貼り付けてから編集したテストケースで問題ありません。

テストでは、外部の依存関係をできるだけ少なくし、できるだけ簡単にする必要があります。テストケースは時間とともに変化する傾向があり、以前はほとんど同じテストケースが突然分岐する場合があります。他のケースを壊すことを心配せずに1つのテストケースを更新することは良いことです。

もちろん、多くのテストケースで同一であり、一斉に変更する必要がある定型コードは、除外することができます。


1
これは主に私が感じる方法です。多くの場合、ほぼ同一のテストコードで十分ですが、繰り返される同一のテストコードは悪いニュースです。
joshin4colours

12

繰り返しはすべての悪の根源です

そうです!繰り返しはすべての悪の根源です。多分それは彼の本で「早すぎる最適化はすべての悪の根源だ」と言っていたクヌースだったが、それは繰り返しだと思う。

プログラムを見たり、プログラムを書いているときに、ある種の繰り返しを発見したときはいつでも、それを削除してください!すぐにそれを殺す...何でもそれを取り除く

私は繰り返しのいくつかの並べ替えを導入し、そこにバグを修正しなければならなかったたびに、私は、レプリカを修正するために忘れてしまった...(ドナルド・クヌース)だから、繰り返しがあるたびに、ちょうどあなたが、できるよう最善のようにそれを削除しないハックありません!

クリーンで無駄のないデザイン(ヘルパークラスに繰り返しコードブロックをカプセル化するなど)を考え、何かを変更する前にテストを作成します(何かを壊さないようにするためです)。これは、記述されたコードに当てはまり、テストコードも例外ではありません。

ここに私を鼓舞するコードホラーからの良い読書があります- コード再利用のコピーアンドペーストスクールのためのささやかな提案


「繰り返しを導入し、そこでバグを修正しなければならなかったたびに、レプリカを修正するのを忘れていました…」。また、c&pを実行して、コピーされたテキストを現在のコンテキストに調整するのを忘れると、これは非常に痛いものになります。バグのあるテストコードは最適な状況のように聞こえませんが、今ではそうですか?
マルクタニ

うん、私はクヌースから駅に行きました:)
ユスボフ

9
あなたは自分自身を繰り返しました:タイトルとあなたのイントロ文で「繰り返しはすべての悪の根源です」と言って自分自身を繰り返しました。
トーマスエディング

ええ、私は意図的に重要性を強調するためにそれをしました、そしてあなたはその部分を編集することを歓迎しています:)
ユスボフ

1
トーマス・エディング、あなたも同じことを繰り返しました。あなたも自分自身を繰り返しました=)
マルクタニ

7

カットアンドペーストするのはまだかなり悪いです。いくつかの問題があります。

コピーアンドペーストされたすべてのコードの変更を必要とする何かに対して脆弱であるため、テストは脆弱になる可能性があります。すべてのテストを書き直す必要がありますか?

ロジックをテスト外のヘルパーメソッドにカプセル化できない場合、それらのヘルパーメソッド自体のテストを記述することはできません。テストをテストするためにコードを壊す必要があるため、テストメソッドのテストを作成することは、通常はやりがいがありません。ただし、ヘルパーメソッド単体テストすることはできます。

テストが読みにくくなる可能性があります。コピーされたコードの大きなブロックは、説明的な名前でヘルパーメソッドを呼び出すよりも読みにくい場合があります。

私がリストしたものはすべて問題の可能性があるものです。それらのどれも実際問題でないとわかった場合、もちろんそれは問題ありません。


>わかりやすい名前でヘルパーメソッドを呼び出します。これで問題にならないのは、ユニットテスト自体がプログラムになりつつあるということです。一部のテストが失敗した場合はどうなりますか?それは壊れているコードですか、それともテストヘルパーですか?
dwjohnston

4

私はかつてあなたに同意していました。しかし、その後、時間の経過とともに、私が行ったすべての変更(特に単体テストでのDIの変更)を変更するには多数のテストが必要であり、面倒であることがわかりました。今では、テストを書いているときでも、DRYの学校に登録しています。

GUIテストの場合、PageObjectパターンを見て、繰り返されるコードを減らすことができます。


2

XUnitパターンを選択することをお勧めします。その本を活用し始めるまで、私はまったく同じ問題を抱えていました。オブジェクト母、それはあなたのシナリオで最も参考になるようなパターンの音。

他の誰かが言ったように、このセットアップコードを適切にカプセル化するのは面倒かもしれませんが、コピーして貼り付けるすべての場所で変更する必要があります。


Object Mother pattern共通の初期化コードの場合は+1 。
k3b

2

人々はできる限り、繰り返しを制限しようとするべきです-はい。しかし、見返りは状況に依存します。これは、「ベストプラクティス」の議論に戻る可能性があります。しかし、問題はこの状況であなたに最適なものです。すべてのルールには例外があります。

私が尋ねるいくつかのことは次のとおりです。1)UATでテストされているこの機能が変更される可能性はどのくらいですか?変更される可能性が低い場合、各コードセットを更新する必要が生じる可能性は低くなります。2)UATに変更がある場合、コピーされたコードの各セットに常に影響しますか、それとも1つまたは2つのセットのみに影響しますか?分離されていて、1つのセットへの変更のみが必要な場合、物事を分離するのに役立つ場合があります。3)すべてのシナリオを処理しようとすると、最初の方法はどの程度複雑になりますか?ネストされたif / else / loopsをたくさん追加していますか?すべての分岐をやりすぎた場合、理解しにくいコードになる可能性があります。すべての分岐ロジックを再検討するよりも、コピーされたテキストのそれぞれで更新を行う方が簡単でしょうか?

コピー/貼り付け/変更がスタックしている場合、「これはメソッドxyzでコピーされます」などのコメントを追加したいと思うでしょう。そうすれば、貼り付けられたすべてのバージョンのコードを更新するように促されます。または(別のSilkTestユーザーから)この繰り返しコードに焦点を当てる別のincファイルを追加できますか。そうすれば、すべてのバリエーションが1か所にあり、更新が必要なさまざまな方法を簡単に確認できます。


0

1つの大きな手順

1つの考え:次のようなメソッドを作成することにより、カットアンドペーストのコードを回避しようとしているように聞こえます。

testScreen(title, fieldList, linkList, param1, param2, param3,...) {
    test that the layout at the top of the screen is correct
    test if PageTitle == title?
    for each field in fieldList:
        check that it appears in order on the screen
    for each field in linkList:
        check that it appears in order on the screen
    test if param1 is whatever...
    test if param2 is whatever...
    etc.
    test that the bottom of the screen is correct
}

多くの小さな手順(ツールキット)

また、反対のアプローチを検討しましたか?1つの大きなtestScreen()プロシージャに100万個のパラメータを渡す代わりに、必要に応じて作成する小さなヘルパープロシージャの独自のフレームワークまたはツールキットを作成することもできます。お気に入り:

testScreenTop()
verifyLinks(list)
testScreenBottom()

これらのプロシージャをすべての画面にカットアンドペーストしますが、コードの小さなチャンクをカットアンドペーストし、カットアンドペーストされていない共通部分(各小さなプロシージャの内容)を切り分けています。

カット&ペースト

カットアンドペーストされたコードが私を噛まなかったのは、コードを変更する前に捨てられたときだけでした。UIテストに関する私の最大の懸念は、テストがどれほど早く廃止されるかです。コードを変更する前にすべてのコードを捨ててしまった場合は、切り取りと貼り付けが可能なニッチを見つけているかもしれません!また、カットアンドペーストされたコードの下流にコードがない場合(アプリケーションのUIなど)、それほど悪くはありません。3行以上を貼り付けている場合は、それについて何かを検討します。少なくとも最小化するための措置を講じてください!

自動化されたUIテスト

ヘック、任意の手法を使用した手動テストよりも自動UIテストで優れた生産性を証明できる場合(自動テストの作成/保守のコストは毎回手動でテストするよりも低くなりますが、品質は同じです)、論文を書くべきだと思います。私はそれを読むでしょう!タイトルを見ることができます。「UIテスト用にコードをカットアンドペーストしてNet Win!」


0

そんなに悪くない。実際、特定のコードパターンが非常に頻繁に使用されていて、変更が非常に日常的である(いくつかの文字列やパラメーター値など)場合は、小さな(-)に基づいて繰り返しテストコードを生成するコードジェネレーターを記述することもできますish?)変更する値の入力リスト。バッチファイル、SQLPlusスクリプト、Excelマクロ(見苦しいが、さまざまなテストスクリプトの変数は既にスプレッドシートに含まれていた)を使用して、これを(テストコードを生成して)何回も実行しました。 。問題は、反復テストケースコードの全体的な構造に何か変更があった場合、必要なものを再生成できることです。


0

これは、他のほとんどの回答と同じですが、技術以外のマネージャーが理解できる方法です。

次のエラーシナリオを想像してください。

  • 誰かがあなたのテストの多くが依存しているデータベーステーブルに変更を加えます。
  • 結果:2933の自動テストのうち117が突然失敗します。

あなたは何をしますか?

  • (1)117個のテストを修正しますか?
  • (2)117個のテストを削除し、新しいコピー&ペーストで再実装します。これは(1)より簡単かもしれません
  • (3)共通のコードを抽出するためにテストをリファクタリングし、将来的にはテストを修正するために1つ(または少数)のメソッドのみを適応する必要がある(@pdrまたは@Michael Brownの回答を参照)
  • (4)テストを再実装せずに117個のテストを削除する

私の経験から:

「コピー&ペーストテスト」などの自動テスト管理を導入すると、短時間で多くのテストを取得できます。

「エラーシナリオ」管理のいくつかが(4)を好むのは、「コピー&ペーストテスト」の修正が非常に高価なためです。

そもそもそれを行う(3)はそれほど速くはないが、テストが生き残る可能性を高める

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.