GUIアプリのUIユニットテストを書いてみましたが、最初に書いたときにうまく機能しているものの、壊れやすく、デザインが変更されるたびに(つまり、頻繁に)壊れるという問題に直面しました。 GUIのメンテナンス可能な単体テストを作成するための一連のガイドラインを見つけるのに苦労しています。
今のところ、私が発見したことの1つは、「このコンポーネントは入力データをどこかに表示する必要がある」というテストが適切であることです(HTMLを使用すると簡単です)。コンポーネントの特定の部分の特定の状態をチェックするテストは、通常、もろいです。 click-click-click-expectのように実行されるテストは、ユーザーの動作と基になるビジネスロジック(最も重要な部分)を追跡しようとしますが、通常は脆弱です。良いテストを書くにはどうしたらいいですか?
より正確に言えば、-howではなく、UIでテストできるwhatに関するいくつかのパターンをテストしたいと思います。命名規則と固定識別子は適切ですが、GUIが大幅に変更されるという中心的な問題は解決しません。変更される可能性が最も低い動作をテストしたいと思います。 テストする適切なものを見つける方法?
GUIテストの一般的な問題...これらのテストが脆弱であると見なされる主な理由は、要件の変更ではないGUIの変更に対応できないであるためです。 GUIの変更がテスト内の単一の場所に分離されるように、テストコードを構造化するように努力する必要があります。
例として、次のようなテストを考えてみましょう。
ユーザーが電話番号フィールドに「999」を入力して保存ボタンをクリックすると、エラーメッセージポップアップに「無効な電話番号」と表示されます。
検証の要件が残っている場合でも、インターフェースを手直しするときにこのテストを中断するための十分な余地があります。
さて、これを少し別の言い回しに入れましょう:
ユーザーが電話番号として「999」を入力してプロファイルページを保存すると、「無効な電話番号」というエラーが表示されます。
テストは同じですが、要件は同じですが、この種のテストはUIの変身後も存続します。もちろん、コードを変更する必要がありますが、コードは分離されます。たとえば、プロファイルページにそのようなテストが10〜20個あり、エラーメッセージを表示する検証ロジックをjavascript-alertsからjquery-popupsに移動した場合でも、エラーメッセージをチェックする単一のテスト部分を変更するだけで済みます。
これはよくある問題です。私は次のことに注意を払います:
要素に名前を付ける方法
要素を識別するには、CSS IDまたはクラスを使用します。オブジェクトが一意である場合は、CSS IDを使用することをお勧めします。 Ruby on Rails the name
属性が自動的に割り当てられ、(非直感的に) CSS IDまたはクラスを使用するよりも良い
要素を識別する方法。
table/tr/td/td
やtd[id="main_vehicle"
などの形式を優先して、td[class='alternates']
のような位置識別子を避けてください。必要に応じて、データ属性の使用を検討してください。上記の場合は、スパンを追加してそれを使用することができるように、<td>
などのレイアウトタグを完全に回避することをお勧めします。 <span id="main_vehicle">
または*[id="main_vehicle"]
などのワイルドカードセレクター。ここで、*
はdiv、span、tdなどにすることができます。
QAとテストにのみ使用されるテスト固有の データ属性 の使用。
要素の不要な修飾を避けます。あなたは自分自身を次のものを使って見つけるかもしれません:
body.main div#vehicles > form#vehicle input#primary_vehicle_name
ただし、これには、入力フィールドが車両の正確なIDを持つフォーム、およびメインのクラスを持つボディとIDを持つフォームの直接の子を持つ車両のIDを持つDIVを持つページに残ることが必要です。車両。その構造のいずれかに変更を加えると、テストが中断します。この場合、あなたはそれを見つけるかもしれません
input#primary_vehicle_name
要素を一意に識別するのに十分です。
表示されるテキストを参照するテストは避けてください。ユーザーに表示されるページのテキストは通常、サイトが維持および更新されるにつれて変化するため、css idやcssクラスやデータ属性などの識別子を使用します。フォームで使用されるform
、input
、select
などの要素も、通常はIDまたはクラスと組み合わせて、要素を識別するのに適した部分です。 li.vehicle
またはinput#first-vehicle
独自の識別子を追加することもできます。 <div data-vehicle='dodge'>
。これにより、開発者や設計者によって変更される可能性が高い要素IDまたはクラスの使用を回避できます。私は実際に、時間の経過とともに、開発者やデザイナーと協力して、名前とスコープについて合意する方がよいことがわかりました。それは難しいです。
固定データを維持する方法。
実際の要素の識別と同様に、ページオブジェクト(変数またはメソッドに保持され、再利用および集中管理が可能な小さなテキスト)を優先して、インラインのハードコードされたセレクターが値を識別しないようにします。ハードコードされた値に対してこのパターンに従うJavaScript変数の例:
storedVars["eqv_auto_year"] = "2015";
storedVars["eqv_auto_make_1"] = "ALFA ROMEO";
storedVars["eqv_auto_make_2"] = "HONDA";`
Selenium wiki および Selenium docs のページオブジェクトの詳細
開発者とのコミュニケーション。
ワークフローの問題である「開発者が変更を行い、QA自動化を壊す」という点での技術的アプローチに関係なく。次のことを確認する必要があります。全員が1つの同じチームであること。開発者は同じ統合テストを実行します。標準は両方のグループによって合意され、それに続いています。 doneの定義には、UIテストの実行と更新が含まれます。開発者とテスターはテスト計画を組んでおり、どちらもチケットグルーミング(アジャイルを行う場合)に参加し、グルーミングの一部としてUIテストについて話します。命名に使用するアプローチと戦略は、アプリケーション開発者と調整する必要があります。同じページが表示されない場合は、オブジェクトの命名について衝突するでしょう。 Rubyプロジェクト用に最近作成したページオブジェクトメソッドの例:
def css_email_opt_in_true
'auto_policy[email_opt_in][value=1]'
end
def css_phone_opt_in
'*[name="auto_policy[phone_opt_in]"]'
end
def css_phone_opt_in_true
'input[name=phone_opt_in][value=true]'
end
def css_credit_rating
'auto_policy[credit_rating]'
end
以下は、JavaScript変数と同じページオブジェクトです。
storedVars["css_email_opt_in"] = "css=*[name='auto_policy[email_opt_in]']";
storedVars["css_phone_opt_in"]="css=*[name='auto_policy[phone_opt_in]']";
storedVars["css_phone_opt_in_true"]="css=input[name='phone_opt_in'][value=true]";
storedVars["css_credit_rating"]="css=select[name='auto_policy[credit_rating]']";
人々が最初にMVC、MVP、プレゼンター、および同様のデザインパターンのようなものを開発した理由は、ビジネスロジックをユーザーインターフェイスから分離するためでした。
当然のことながら、ビューパーツはプログラムを起動し、表示内容を確認することによってのみテストできます。つまり、受け入れテストでのみテストできます。
一方、ビジネスロジックのテストは、単体テストで実行できます。そして、それがあなたの質問への答えです。モデル内のすべてをテストし、可能であれば、コントローラーのコードもテストできます。
GUIは大きく変わります
これは、要件を変更したときにのみ発生します。要件が変更された場合、コードを変更する以外に回避策はありません。優れたデザインとアーキテクチャを作成できた場合、変更は多くの場所に反映されません。
GUIインタラクションテストは、他のどの種類のテストよりも多かれ少なかれ脆いはずです。あれは;アプリケーションが何らかの形で変化している場合は、それを反映するようにテストを更新する必要があります。
比較として:
単体テスト
元の:validateEmail()
はInvalidData
例外をスローする必要があります。ユニットテストで正しくカバーされています。
変更:validateEmail()
はInvalidEmail
例外をスローする必要があります。これでテストが正しくなくなり、更新し、すべてが再び緑色になります。
GUIテスト
元の:無効なメールを入力すると、「無効なデータが入力されました」というポップアップエラーボックスが表示されます。テストによって正しく検出されました。
変更:無効なメールを入力すると、「無効なメールが入力されました」というインラインエラーが発生します。これでテストが正しくなくなり、更新し、すべてが再び緑色になります。
入力と出力をテストしていることを思い出してください-明確に定義された動作です。 GUIテスト、ユニットテスト、統合テストなどに関係なく.