yfj2’s Automatic Web Test Related Blog

yfj2のWEBテスト自動化に関わるブログ

【Geb】モジュールとは? / What isModules ?

【Geb】モジュールとは? / What isModules ?
著者:ふじさわゆうき

この記事では、以下を目的としています。

  • Gebのモジュールについて公式ページの翻訳を通して理解すること

*Gebの開発環境構築については以下の記事を参照してください

目次

  1. 原文情報
  2. 原文と翻訳
  3. まとめ

原文と翻訳

目次


6 Modules
6.1 Base And Context / Base と Context
6.2 Reusing modules across pages / ページ間でのmoduleの再利用
6.3 Using modules for repeating content on a page / ページ上の同一contentにmoduleを利用する
6.4 The Content DSL / The Content DSL
6.5 Inheritance / 継承
6.6 Size and Location / サイズと位置

6. Modules

Modules are re-usable definitions of content that can be used across multiple pages. They are useful for modelling things like UI widgets that are used across multiple pages, or even for defining more complex UI elements in the one page.
Moduleは、複数のページにまたがって使用することができるコンテンツの再利用可能な定義である。
Moduleは、1ページ内に、より複雑なUI要素を定義するための複数のページにまたがって使用されているUIウィジェットのようなものをモデル化するためにも有用である。

They are defined in a manner similar to pages, but extend Module…
以下、moduleは、ページと同様に定義されるが、moduleを拡張している

class ExampleModule extends Module {
    static content = {
        button { $("input", type: "submit") }
    }
}

Pages can “include” modules using the following syntax…
以下のように、ページは、次の構文を使用してmoduleを「含む」ことができる

class ExamplePage extends Page {
    static content = {
        theModule { module ExampleModule }
    }
}

The module method is a special method only available in content template definitions. It sets the content to an instance of the module…
以下のように、moduleのメソッドは、contentのテンプレート定義のみで使用することができる特殊なメソッドである。これは、moduleのインスタンスにcontentを設定する

Browser.drive {
    to ExamplePage
    theModule.button.click()
}


Modules can also be parameterised
モジュールはパラメータ化することもできる

class ExampleModule extends Module {
    def buttonName
    static content = {
        button { $("input", type: "submit", name: buttonName) }
    }
}

Where the parameters are set using the module method
以下、パラメータをmoduleメソッドを使用して設定する場合

class ExamplePage extends Page {
    static content = {
        theModule { name -> module ExampleModule, buttonName: name }
    }
}
 
Browser.drive {
    to ExamplePage
    theModule("something").button.click()
}

Modules can also include other modules
Moduleは、他のModuleも含むこともできる

class ExampleModule extends Module {
    static content = {
        innerModule { module InnerModule }
    }
}
 
class InnerModule extends Module {
    static content = {
        button { $("input", type: "submit") }
    }
}
 
class ExamplePage extends Page {
    static content = {
        theModule { module ExampleModule }
    }
}
 
Browser.drive {
    theModule.innerModule.button.click()
}

6.1 Base と Context

Modules can be localised to a specific section of the page that they are used in, or they can specify an absolute context as part of their definition. There are two ways that a modules base/context can be defined.
Moduleは、Moduleが使用されているページの特定のセクションに局所化することができる。加えて、それらの定義の一部として絶対的なコンテキストを指定することもできる。module base/contextを定義するには2つの方法がある

It can be defined at inclusion time…
以下のようにModuleをインクルード時に定義することができる

static content = {
    form { module FormModule, $("form") }
}

We can define a Navigator context when including the module using the above syntax. This now means that all $() function calls that occur within the module are against the given context (in this case, the form element).
上記の構文を使用してmoduleを含めることで、Navigatorのcontextを定義することができます。これはmoduleで発生するすべての$()関数呼び出し、指定されたmoduleに反していることを意味します(この場合、フォーム要素で)。

import geb.Module
 
class FormModule extends Module {
    static base = { $("form") }
}

This has the same effect as the code above.
They can also be combined. Consider the following HTML…
これは、上記のコードと同じ効果を有する。
これらを組み合わせることもできる。次のHTMLを考えてみよう

<div class="a">
    <form>
        <input name="thing" value="a"/>
    </form>
</div>
<div class="b">
    <form>
        <input name="thing" value="b"/>
    </form>
</div>

And the following content definitions…
次に、以下の内容の定義を考えてみよう

import geb.*
 
class ExamplePage extends Page {
    static content = {
        formA { module FormModule, $("div.a") }
        formB { module FormModule, $("div.b") }
    }
}
 
class FormModule extends Module {
    static base = { $("form") }
    static content = {
        thingValue { thing().value() }
    }
}

When working with a browser at a ExamplePage page…
Exampleページでブラウザを操作するとき以下の通り

assert formA.thingValue == "a"
assert formB.thingValue == "b"

If the module declares a base, it is always calculated relative to the base given by the including statement. If the including statement does not specify a base, the module’s base is calculated relative to the including page’s base.
moduleがbaseを宣言している場合、moduleは常に含めたbaseに所属しているとみなされます。しかし、仮に含めた文がbaseを特定しない場合、moduleのbaseはページのbaseに所属しているとみなされます。

6.2 ページ間でのmoduleの再利用

As previously stated, modules can be used to model page fragments that are reused across multiple pages. For example, many different types of pages in your application may show information about the user’s shopping cart. You could handle this with modules.
前述のように、moduleは、ページの一部としてモデル化することができる。そうすることで、複数のページにわたって再利用することができる。例えば、アプリケーション内の多くの異なる種類ページで、ユーザーのショッピングカートについての情報が表示されることがあります。これをモジュールで扱うことができる。

class CartInfoModule extends Module {
    static content = {
        section { $("div.cart-info") }
        itemCount { section.find("span.item-count").toInteger() }
        totalCost { section.find("span.total-cost").toDouble() }
    }
}
 
class HomePage extends Page {
    static content = {
        cartInfo { module CartInfoModule }
    }
}
 
class OtherPage extends Page {
    static content = {
        cartInfo { module CartInfoModule }
    }
}

6.3 ページ上の同一contentにmoduleを利用する

Other than content that is repeated on different pages (like the shopping cart mentioned above), pages also have content that is repeated on the page itself. On a checkout page, the contents of the shopping cart could be summarized with the product name, the quantity and price for each product contained. For this kind of page, a list of modules can be collected using the moduleList function.
ページ上で再利用されるcontent(前述のショッピングカートのような)以外に、ページは、ページ自体に繰り返されたコンテンツを持っている。チェックアウトページでは、ショッピングカートの内容は、製品ごとの製品名と数量と価格でまとめることができる。このようなページは、moduleList関数を使用することでmoduleリストを収集することができる。

We can model one line of the table like this:
カートの内容については、次のHTMLを考えてみよう:

<table>
    <tr>
        <th>Product</th><th>Quantity</th><th>Price</th>
    </tr>
    <tr>
        <td>The Book Of Geb</td><td>1</td><td>5.99</td>
    </tr>
    <tr>
        <td>Geb Single-User License</td><td>1</td><td>99.99</td>
    </tr>
    <tr>
        <td>Geb Multi-User License</td><td>1</td><td>199.99</td>
    </tr>
</table>

We can model one line of the table like this:
このようなテーブル行をモデル化することができる

class CartRow extends Module {
    static content = {
        cell { $("td", it) }
        productName { cell(0).text() }
        quantity { cell(1).text().toInteger() }
        price { cell(2).text().toDouble() }
    }
}

And define a list of CartRows in our Page:
そして、私たちのページでCartRowsリストを定義する:

class CheckoutPage extends Page {
    static content = {
        cartItems { moduleList CartRow, $("table tr").tail() } // tailing to skip the header row
    }
}

Because the return value of cartItems is a list of CartRow instances, we can use any of the usual collection methods:
cartItemsの戻り値がCartRowインスタンスのリストであるため、我々は、通常の収集方法のいずれかを使用できます。

assert cartItems.every { it.price > 0.0 }

We can also access the cart items like this:
このようなカートのアイテムにアクセスすることができる

assert cartItems[0].productName == "The Book Of Geb"

Unfortunately, this has a performance penalty of creating all modules in the list. You can get around it and add support for ranges by changing your content definition to:
残念ながら、これはリスト内のすべてのmoduleを作成することでパフォーマンスに悪影響を与える。しかし、コンテンツ定義を以下のように変更することで上手く避けることができる

class CheckoutPage extends Page {
    static content = {
       cartItems { index -> moduleList CartRow, $("table tr").tail(), index }
    }
}

Now all of the following will pass and is more efficient:
これで全てが上手くいき、より効率的になるだろう:

assert cartItems.every { it.price > 0.0 }
assert cartItems(0).productName == "The Book Of Geb"
assert cartItems(1..2)*.productName == ["Geb Single-User License", "Geb Multi-User License"]

Keep in mind that you can also pass module parameters the same way as you would with the module() method:
あなたは、module() メソッドと同じようにモジュールパラメータも渡すことができることも覚えておくこと

static content = {
    myContent { index -> moduleList MyModule, $(".myModuleClass"), index, myParam: 'param value' }
}

6.4 The Content DSL

The Content DSL used for modules is exactly the same as the one used for pages, so all of the same options and techniques can be used.
moduleで利用されるThe Content DSLは、"5.3 The Content DSL"と全く同様である。したがって、同様のオプションと技術を利用することができる。
http://www.gebish.org/manual/current/pages.html#the_content_dsl

6.5 継承

Modules can use inheritance in the same way that pages can. That is, their content definitions are merged with any content redefined in the subclass taking precedence of the superclass.
Moduleは、ページと同様に継承を使用することができます。つまり、そのコンテンツ定義はサブクラスで再定義されたコンテンツにマージされます。

6.6 サイズと位置

You can obtain the size and location of the module. All units are in pixels. The size is available via the height and width properties, while the location is available as the x and y properties which represent the distance from the top left of the page (or parent frame) to the top left point of the base of the module.
moduleのサイズと位置を取得することができる。全てピクセル単位である。サイズは高さと幅のpropertyを介して利用可能である。一方で、位置は、ページ(または親フレーム)左上からmodule左上の点への距離を表した"x"と"y"のpropertyとして利用可能です。

$("div").height == 20
$("div").width == 40
$("div").x == 60
$("div").y == 80