Galen Frameworkを使用したビジュアルテスト。 コードの読みやすさの改善

Galen Frameworkに関する最初の記事 書かれてから2年が経ちました。 当時、ガレンはすべて、ページ要素を他の要素に対して相対的に配置するための単純な一連のチェックでした。 それから、スクリーンショットをピクセルごとにチェックする方法も、実際にはフレームワークの基礎であるガレン仕様言語を拡張する方法もありませんでした。 また、テストは、テストスイートの1つの形式を使用してのみ実行でき、ガレンテストの機能が大幅に制限されていました。 それ以来、コミュニティのサポートにより、ガレンでは多くのことが変わりました。 現在、それは既に視覚テスト用の本格的なツールであり、スクリーンショットをピクセルごとにチェックし、テストされた画像にフィルターを課すだけでなく、 Galen Specs言語の機能を拡張できる豊富な機能セットも提供します。 この記事では、 このページの例を使用して、Galen Specs言語の新機能をデモンストレーションし、Galen Frameworkで視覚テストの読みやすさを改善する方法を示します







読みやすさは、テストコードの重要なプロパティの1つです。 私の従業員の1人は、テストの可読性がメインコードの可読性よりも重要だとさえ主張しました。 テストは、アプリケーションの機能がどのように機能するかを理解しようとするときのエントリポイントになります。 このアイデアが気に入ったので、Galen Frameworkの視覚テストに適用することにしました。 私の目標は、さまざまなブラウザーサイズでサイトをどのように表示するかが明らかになるテストを作成することでした。 アダプティブWebサイトのレイアウトをテストする際の最も一般的なケースを見て、テストを改善する方法を考えてみましょう。



Galen Frameworkの最初のバージョンがリリースされて以来、理解できる視覚テストを作成しようとする私の試みはすべて失敗しました。 最初はテストコードは理解できましたが、ページ上の要素の数が10を超えるとすぐに、これらの大量の命令セットを理解して維持することが難しくなりました。

最も一般的な例の1つは、水平方向に間隔を空けて繰り返される要素(メニューなど)です。







デスクトップ解像度のテストサイトは次のようになります。







標準の手順を使用してガレンの水平メニュー項目をテストするには、同様のコードを記述する必要があります。



@objects menu #menu ul item-* li a = Menu = @forEach [menu.item-*] as menuItem, next as nextItem ${menuItem}: left-of ${nextItem} 0 to 5px aligned horizontally all ${nextItem}
      
      







ご覧のとおり、このコードは、すべてのメニュー項目がソートされる最も一般的なサイクルです。 各メニュー項目は次の項目に対してチェックされ、上端と下端の位置合わせ、および要素間の距離がチェックされます。 このコード自体は非常に理解しやすいように見えますが、1つのファイルで多くの同様のブロックが使用されるとすぐに、それを把握することが難しくなります。



このテストを実行して、レポートを確認します。



 galen check homepage.gspec --url http://galenframework.github.io/galen-extras/website/index.html --size 1024x768 --htmlreport reports
      
      







その結果、次のレポートが得られます。







あまり有益ではありません。 このようなレポートを確認したり、テストコードを掘り下げたりすると、常に疑問が生じます。なぜこのチェックが選択されたのですか? ここで正確に何をテストしたいですか? など



では、このコードをどのように改善できますか? いわゆるカスタムルールが助けになります。 要するに、これは関数に似たものであり、ユーザーが作成した通常の文からパラメーターのみが解析されます。 とりあえず、後でパラメーター化をそのままにして、最も簡単なルールを最初に実装してみましょう。 たとえば、上記の場合、 「メニュー項目は水平方向に並んでいる」という表現を作成できます。 少し後、この式にパラメーターを追加します。 そして、メインのテストファイルでわくわくしないように、 my-rules.gspecファイルに転送します



 @rule menu items are aligned horizontally next to each other @forEach [menu.item-*] as menuItem, next as nextItem ${menuItem}: left-of ${nextItem} 0 to 5px aligned horizontally all ${nextItem}
      
      







メインファイルのこのコードの代わりに、次のようにこの関数を呼び出すことができます



 @import my-rules.gspec @objects menu #menu ul item-* li a = Menu = | menu items are aligned horizontally next to each other
      
      







もう1つの良い点は、レポートで同じ提案が表示され、そのすべてのチェックがその下にグループ化されることです。 したがって、レポートも読みやすくなります。







これで、パラメーターを追加して式を改善できます。 たとえば、ページを見ると、その上にさらにbox



要素が表示され、このチェックも適用されます。 異なるオブジェクトに同じ式を使用すると便利です。 式をこれに変更して、 objectPattern



margin



置き換えてみましょう:



 @rule %{objectPattern} are aligned horizontally next to each other with %{margin} margin @forEach [${objectPattern}] as item, next as nextItem ${item}: left-of ${nextItem} ${margin} aligned horizontally all ${nextItem}
      
      







次に、テストを変更します。



 @import my-rules.gspec @objects menu #menu .middle-wrapper item-* ul li box-* .box-container .box .panel = Menu = | menu.item-* are aligned horizontally next to each other with 0 to 5px margin = Main Section = | box-* are aligned horizontally next to each other with ~ 30px margin
      
      







すでに良い。 私が嫌いなのは、これらすべての式で-*



文字を使用することだけです。 @ruleに転送するのは賢明な決定ではありません。 設計の柔軟性が侵害されます。 しかし、この場合は特に、テスト自体でオブジェクトグループ化を使用できます。 次のように機能します。 新しい@groupsセクションオブジェクトを宣言した後、オブジェクトのグループを指定し、各グループの名前を見つけて、結合するオブジェクトをリストできます。 ここでは、2つのグループmenu_itemsboxを作成します。



 @import my-rules.gspec @objects menu #menu .middle-wrapper item-* ul li box-* .box-container .box .panel @groups menu_items menu.item-* boxes box-*
      
      







これで、検索式menu.item-*



およびbox-*



代わりに、 &



記号を使用してグループを指定できます



 = Menu = | &menu_items are aligned horizontally next to each other with 0 to 5px margin = Main Section = | &boxes are aligned horizontally next to each other with ~ 30 px margin
      
      







これでチェックがさらに読みやすくなりました。 ただし、テストしているページは応答性が高く、チェックは大画面でのみ機能します。 モバイル解像度(幅450ピクセルなど)でレイアウトを確認すると、メニュー項目が2列の表に表示されていることがわかります。







また、ボックス要素は水平方向ではなく垂直方向に配置されます。 これらの新しい式を実装してみて、最終的に同様のものを取得します。



 # ... = Menu = @on desktop, tablet | &menu_items are aligned horizontally next to each other with 0 to 5px margin @on mobile | &menu_items are rendered in 2 column table layout, with 0px vertical and 0 to 4px horizontal margin = Main Section = @on desktop, tablet | &boxes are aligned horizontally next to each other with ~30 px margin @on mobile | &boxes are aligned vertically above each other with 20px margin
      
      







ボックスのモバイルレイアウトの要素の垂直配置の検証は、水平からコピーしてわずかに修正できます。



 # ... @rule %{objectPattern} are aligned vertically above each other with %{margin} margin @forEach [${objectPattern}] as item, next as nextItem ${item}: aligned vertically all ${nextItem} above ${nextItem} ${margin}
      
      







しかし、メニュー項目の表形式のレンダリングでは、もう少し複雑になります。 JavaScriptコードを使用してこのチェックを実装してみましょう。 これを行うには、 my-rules.js



ファイルを作成し、 my-rules.js



ファイルにインポートしmy-rules.gspec







 @script my-rules.js # ...
      
      







さて、すでにmy-rules.jsファイルに次のコードを記述します。 組み込みのfindAll関数を使用します。これは、検索式で指定されたすべての要素を検索します。



 function _ruleRenderedInTable(rule, itemPattern, columns, verticalMargin, horizontalMargin) { var allItems = findAll(itemPattern); var currentColumn = 0; for (var i = 0; i < allItems.length - 1; i += 1) { if (currentColumn < columns - 1) { //          rule.addObjectSpecs(allItems[i].name, [ "left-of " + allItems[i + 1].name + " " + horizontalMargin, "aligned horizontally all " + allItems[i + 1].name ]); } var j = i + columns; if (j < allItems.length) { //      ,    rule.addObjectSpecs(allItems[i].name, [ "above " + allItems[j].name + " " + verticalMargin, "aligned vertically all " + allItems[j].name ]); } currentColumn += 1; if (currentColumn === columns) { currentColumn = 0; } } } rule("%{itemPattern} are rendered in %{columns: [0-9]+} column table layout, with %{verticalMargin} vertical and %{horizontalMargin} horizontal margin", function (objectName, parameters) { _ruleRenderedInTable(this, parameters.itemPattern, parseInt(columns), parameters.verticalMargin, parameters.horizontalMargin); });
      
      







%{columns: [0-9]+}



の構文に注意してください。 この場合、標準の.*



代わりに独自の正規表現[0-9]+



を指定します.*



これは、 columns



パラメーターに使用されます。 これは必須ではありませんが、この方法でタイプミスを防ぎます。 3つの異なる組版のチェックを追加したため、1つのコマンドですべての仮想デバイス(デスクトップ、タブレット、モバイル)でテストを実行するには、テストスイートを準備する必要があります。 my.testファイルを作成します。



 @@ table devices | tag | size | | desktop | 1024x768 | | tablet | 800x600 | | mobile | 500x700 | @@ parameterized using devices Home page test on ${tag} device http://galenframework.github.io/galen-extras/website/index.html ${size} check homepage.gspec --include "${tag}"
      
      







これで、コマンドを使用してテストを実行できます



 galen test my.test --htmlreport reports
      
      







その結果、次のレポートが取得されます。











次は?





さまざまなレイアウトでの長い実験の後、 Galen Extrasプロジェクトを作成しました。 このプロジェクトでは、最も一般的なケースの拡張機能を実装しました。 メインコードはgalen-extras-rules.gspecおよびgalen-extras-rules.jsファイルにあります



他のすべての特別な表現の開発については詳しく説明せず、最終的に何が起こったのかを示します。 以下は、ページ上のすべての要素をチェックするテストです。



 @import galen-extras/galen-extras-rules.gspec @objects header #header .middle-wrapper box-* .box-container .box .panel menu #menu .middle-wrapper item-* ul li content #content greeting #content h1 footer #footer .middle-wrapper @groups (box, boxes) box-* (menu_item, menu_items) menu.item-* skeleton_elements header, menu, content, footer skeleton_element &skeleton_elements image_validation header, menu.item-* = Skeleton = | &skeleton_elements sides are inside screen with 0px margin from top and bottom | &skeleton_elements are aligned vertically above each other with 0 to 1px margin = Page is centered horizontally inside screen with 900px on desktop, but on mobile and tablet it stretches to screen = | every &skeleton_element is centered horizontally inside screen @on desktop | every &skeleton_element has width 900px @on mobile, tablet | every &skeleton_element is aligned vertically all screen = Menu items should adapt layout = @on * | amount of visible &menu_items should be 4 | every &menu_item is inside menu and has height ~ 64px | first &menu_item is inside menu 0px top left @on desktop, tablet | &menu_items are aligned horizontally next to each other with 0 to 5px margin @on mobile | &menu_items are rendered in 2 column table layout, with 0px vertical and 0 to 4px horizontal margin = Main Section = = Greeting = greeting: height 30 to 60px inside content 40px top, 20px left right @on * | amount of visible &boxes should be 3 | test all &boxes with box-component.gspec @on desktop, tablet | &boxes are aligned horizontally next to each other with equal distance | &box sides are inside content with 20px margin from left and right | every &box is below greeting ~ 10px | every &box is above footer > 19px @on mobile | &boxes are aligned vertically above each other with 20px margin | every &box is inside content 20px left right | first &box is below greeting ~ 10px | last &box is above footer > 19px
      
      







ご覧のとおり、すべてのケースが特殊な式の作成に成功したわけではありません。 たとえば、 「ページは画面内で水平方向に900pxで中央揃えされています...」セクションでは、チェックをいくつかの部分式に分割する必要がありました。 残念ながら、この特定のケースのすべての種類の特別な表現を書くのは難しいように思えました。 構造が長すぎるため、タイプミスの可能性が高くなります。 しかし、これらすべての表現を拡大または改善する方法についてアイデアをお持ちの場合は、リクエストのプールを聞いてうれしいです。



ちなみに、レイアウトのバグがあるサイトのバージョンも用意しました。







このバージョンのサイトに対して同じテストを実行すると、レポートに次のエラーが表示されます。







おわりに





ご覧のとおり、 @ rule機能を慎重に使用すると、非常に優れた結果を達成し、テストコードを大幅に削減し、テストサポートを改善できます。 また、このアプローチは、サイトの開発においてTDDに簡単に適用できます。 さらに重要なことは、このようなコードは、サイトの外観と場所を説明する明確なドキュメントになります。 次回の記事では、一方でGalen Frameworkを検討し、画像のピクセルごとの比較のためのさまざまな手法について詳しく説明します。



参照資料





  1. Galen Extrasドキュメント
  2. 完全なGalen仕様言語ドキュメント
  3. GitHubのGalenフレームワーク



All Articles