GUIの壊れやすいではなく、保守可能なユニットテストを記述する方法


16

GUIアプリのUIユニットテストを作成してみましたが、最初に作成したときにはうまく機能しますが、設計が変更されるたびに(つまり、非常に頻繁に)壊れるという問題に直面しています。GUIの保守可能な単体テストを作成するためのガイドラインを見つけるのに苦労しています。

今のところ、私が発見したことの1つは、「このコンポーネントは入力データをどこかに表示する必要がある」というテストが適切であることです(HTMLでは非常に簡単です)。通常、コンポーネントの特定の部分の特定の状態を確認するテストは脆弱です。クリック-クリック-クリック-期待のようなテストは、ユーザーの行動と基礎となるビジネスロジック(最も重要な部分)を追跡しようとするため、通常は脆弱です。良いテストを書くにはどうすればいいですか?


もっと正確に言うと、UIでをテストできるかについて、正確なテスト方法ではなく、いくつかのパターンを知りたいです。命名規則と固定識別子は優れていますが、コアの問題、つまりGUIが大きく変わるという問題は解決しません。変化する可能性が最も低い動作をテストしたいと思います。テストする正しいものを見つける方法は?


1
@MichaelDurrant:あなたは質問を編集して、一般にUIテストに関する質問を作成しましたが、私はもともと単体テストについて質問しました。統合テストは保守が難しく、可能な場合は単体テストを優先します。
mik01aj

2
これは問題の一部だと思いますが、基本的には、ユニットテストだけではインターフェイスをテストすることはできません。この点でGUIは変わりません。
jk。

m01、元に戻すことができます。UIテストは通常​​、統合テストであると考えています。テストは、存在するシードとフィクスチャのデータと、それらと連携するUIに依存する傾向があります。優れた他のデータに依存しない真のUIテストがある場合。しかし、これは比較的まれであることがわかりました。
マイケルデュラント

2
:これは、GUIのテストについてですので、関連しているが重複していないprogrammers.stackexchange.com/questions/109703/...
K3B

回答:


3

GUIテストの一般的な問題...これらのテストが脆いと考えられる主な理由は、要件の変更ではないGUIの変更に耐えることができないためです。GUIの変更がテストの単一の場所に隔離されるように、テストコードを構造化するよう努力する必要があります。

例として、次のように表現されるテストを考えてみましょう。

ユーザーが「電話番号」フィールドに「999」と入力して「保存」ボタンをクリックすると、エラーメッセージのポップアップに「無効な電話番号」と表示されます。

検証の要件が残っている場合でも、インターフェイスを再作成すると、このテストが中断する余地が十分にあります。

さて、これを少し別の言い回しにしましょう。

ユーザーが電話番号として「999」を入力し、プロファイルページを保存すると、「無効な電話番号」というエラーが表示されるはずです。

テストは同じで、要件は同じですが、この種のテストはUIの変更後も存続します。もちろん、コードを変更する必要がありますが、コードは分離されます。プロフィールページにこのようなテストを10個または20個持っていて、エラーメッセージを表示する検証ロジックをjavascript-alertsからjquery-popupsに移動した場合でも、エラーメッセージをチェックする単一のテストパーツを変更するだけです。


4

これはよくある問題です。私はに注意を払うでしょう:

  • 要素に名前を付ける方法

    CSS IDまたはクラスを使用して要素を識別します。オブジェクトが一意である場合、CSS IDを使用することを推奨します。あなたが使用しているフレームワークを考えてください。例えば、Ruby on Railsでは、name属性は自動的に割り当てられ、CSS IDまたはクラスを使用するよりも(非直感的に)より良い場合があります

  • 要素を識別する方法。

    table/tr/td/tdなどの形式を好むような位置識別子は避けてください。必要に応じて、データ属性の使用を検討してください。さらに良いことのような回避のレイアウトタグにしてみてください完全ので上記のいずれかのスパンと使用することを、例えば追加することができるためか、などのワイルドカードセレクタところ、今のdiv、スパン、TDなど可能性をtd[id="main_vehicle"td[class='alternates']<td><span id="main_vehicle">*[id="main_vehicle"]*

  • qaおよびテストにのみ使用されるテスト固有のデータ属性を使用します。

  • 要素の不必要な修飾を避けます。以下を使用して自分自身を見つけるかもしれません:

    body.main div#vehicles > form#vehicle input#primary_vehicle_name

    ただし、これには、入力フィールドがvehicleの正確なIDを持つフォームに、mainのクラスを持つbodyとidを持つフォームの直接の子を持つvehicleのdivを持つdivのページに残る必要があります車両。その構造のいずれかに対する変更とテストの中断。この場合、あなたはそれを見つけるかもしれません

    input#primary_vehicle_name

    要素を一意に識別するのに十分です。

  • 目に見えるテキストを参照するテストは避けてください。ユーザーに表示されるページ上のテキストは、通常、サイトが維持および更新されると時間とともに変化するため、css idやcssクラスやデータ属性などの識別子を使用します。などの要素forminputおよびselectフォームで使用は、通常、例えば、IDやクラス、との組み合わせで、また、特定の要素の良い部分ですli.vehicleinput#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自動化を中断する」という点での技術的アプローチに関係なく。次のことを確認する必要があります。全員が同じチームです。開発者は同じ統合テストを実行します。両方のグループが標準に合意し、順守しています。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]']";
    

2
これらは有用なヒントであり、私は実際にそれらのほとんどを既に追っています。私の問題は、いくつかのボタンのテストを作成すると、同じアクションが他の場所から処理されている間にこのボタンが削除されることでした。または、ボタンはそのままですが、ラベルが変更され、ボタンは追加のアクションも実行します。
mik01aj

1
ああ、それは知っておいてよかった。うん、私はワークフローと組織のQA-開発者の相互作用に焦点を当てるその原料について
マイケル・デュラント

1
また、あなたの質問を見つけて、あなたが持っているすべてのピースを所持していない、または知らないかもしれない他の人のために情報を投稿しています。
マイケルデュラント

1
フィードバックに基づいて、最後のポイントを拡張しました。
マイケルデュラント

1
そして、要素の命名と識別、および可視テキストの使用に関する部分を更新しました。
マイケルデュラント

3

最初にMVC、MVP、プレゼンター、および同様のデザインパターンなどを開発した理由は、ビジネスロジックをユーザーインターフェイスから分離するためです。

明らかに、ビュー部分は、プログラムを起動し、表示されているものを確認することによってのみテストできます。つまり、受け入れテストでのみテストできます。

一方、ビジネスロジックのテストは単体テストで実行できます。それがあなたの質問への答えです。モデル内のすべてをテストします。必要に応じて、コントローラーのコードもテストできます。


GUIは大きく変わります

それは、要件が変化した場合にのみ発生します。要件が変更された場合、コードを変更する以外に回避する方法はありません。優れた設計とアーキテクチャを作成できた場合、変更は多くの場所に反映されません。


2
良い点だと思います。たぶん、MVCが間違っているだけかもしれません:)ところで、多くのユーザー向けのWebプラットフォームを開発する場合、要件の変更は避けられないと考えています。ユーザーがGUIの使用を開始するまで、ユーザーの動作はわかりません。
mik01aj

2

GUI相互作用テストは、他の種類のテストよりも脆弱ではないはずです。あれは; アプリケーションが何らかの方法で変更されている場合は、テストを更新してそれを反映する必要があります。

比較として:

単体テスト

オリジナル:例外validateEmail()をスローする必要がありInvalidDataます。これはユニットテストで正しく説明されています。

変更:例外validateEmail()をスローする必要がありInvalidEmailます。これでテストが不正確になり、更新して、すべてが再び緑色になります。

GUIテスト

オリジナル:無効な電子メールを入力すると、「無効なデータが入力されました」というポップアップエラーボックスが表示されます。テストで正しく検出されました。

変更:無効な電子メールを入力すると、「入力された無効な電子メール」を含むインラインエラーが発生します。これでテストが不正確になり、更新して、すべてが再び緑色になります。


入力と出力をテストしていることに注意してください-いくつかの明確に定義された動作。GUIテスト、単体テスト、統合テストなどに関係なく

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