yfj2’s Automatic Web Test Related Blog

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

【入門】Geb+SpockではじめるWebテスト~リファクタリング編~

【入門】Geb+SpockではじめるWebテスト~リファクタリング編~
著者:ふじさわゆうき

この記事は、以下の記事の続きです。
初めて訪問した方は以下の記事を参照してください。

目次

  1. 今回の目的
  2. GoogleWikipediaTestの問題点
  3. 問題解決とリファクタリング(1)
  4. 問題解決とリファクタリング(2)
  5. 問題解決とリファクタリング(3)
  6. まとめ

1. 今回の目的

Geb本家で示されているテスト例のもとに作成した"GoogleWikipediaTest"の問題点を洗い出してリファクタリングすること
本家:http://www.gebish.org/manual/current/intro.html#testing

2. GoogleWikipediaTestの問題点

GoogleWikipediaTestの問題点を分析してみたところ、以下問題点があった。

  1. テストケース"GoogleWikipediaMainTest.groovy"がModuleなどの内部実装を意識しなければならない作りになっている
    • 「search.field.value("wikipedia")」は、「search("wikipedia")」にすれば内部実装を意識しなくてよくなる
    • 「firstResultLink.click()」は、「clickFirstResultLink」にすれば内部実装を意識しなくてよくなる
    • など
  2. GoogleSearchModuleのbuttonの指定が間違っている。サンプルでは実行していないのでエラーが発生していないだけ。

以下、現状のgoogle検索ボタンのHTML。inputタグは使用していない。

<button class="gbqfb" aria-label="Google Search" name="btnG" id="gbqfb">
	<span class="gbqfi gb_Sa"></span>
</button>

3. 部分一致のキーワード検索テストができない
以下のようなコードにするとエラーが発生する

class GoogleWikipediaMainTest extends GebSpec {

	def "first result for wikipedia search should be wikipedia"() {
		given:
		to GoogleHomePage

		expect:
		at GoogleHomePage

		when:
		search.field.value("test")

		then:
		waitFor { at GoogleResultsPage }

		when:
		search.field.value("wikipedia")
		
		then:
		waitFor { at GoogleResultsPage }

		and:
		firstResultLink.text() == "Wikipedia"

		when:
		firstResultLink.click()

		then:
		waitFor { at WikipediaPage }
	}
}

3. 問題解決とリファクタリング(1)

問題

  • テストケース"GoogleWikipediaMainTest.groovy"がModuleなどの内部実装を意識しなければならない作りになっている
    • 「search.field.value("wikipedia")」は、「search("wikipedia")」にすれば内部実装を意識しなくてよくなる
    • 「firstResultLink.click()」は、「clickFirstResultLink」にすれば内部実装を意識しなくてよくなる

リファクタ手順

  1. GoogleHomePageに"search"メソッドを追加する
  2. GoogleWikipediaMainTestの"search.field.value"を"search"に修正する
  3. GoogleWikipediaMainTestを実行して動作確認
  4. GoogleResultsPageに"clickFirstResultLink"メソッドを追加する
  5. GoogleWikipediaMainTestの"firstResultLink.click"を"firstResultLinkClick()"に修正する
  6. GoogleWikipediaMainTestを実行して動作確認

実装

  • "search"メソッドを追加する
class GoogleHomePage extends Page {

	// pages can define their location, either absolutely or relative to a base
	static url = "http://google.com/ncr"

	// “at checkers” allow verifying that the browser is at the expected page
	static at = { title == "Google" }

	static content = {
		// include the previously defined module
		search { module GoogleSearchModule, buttonValue: "Google Search" }
	}
        
        //"search"メソッドを追加
	public void search(String keyword){
		search.field.value(keyword)
	}
}
  • "search.field.value"を"search"に修正
class GoogleWikipediaMainTest extends GebSpec {

	def "first result for wikipedia search should be wikipedia"() {
		given:
		to GoogleHomePage

		expect:
		at GoogleHomePage

		when:
                //"search.field.value"を"search"に修正
		search("wikipedia")

		then:
		waitFor { at GoogleResultsPage }

		and:
		firstResultLink.text() == "Wikipedia"

		when:
		firstResultLink.click()

		then:
		waitFor { at WikipediaPage }
	}
}
  • "clickFirstResultLink"メソッドを追加する
class GoogleResultsPage extends Page {
	static at = { title.endsWith "Google Search" }
	static content = {
		// reuse our previously defined module
		search { module GoogleSearchModule, buttonValue: "Search" }

		// content definitions can compose and build from other definitions
		results { $("div.g") }
		result { i -> results[i] }
		resultLink { i -> result(i).find("a") }
		firstResultLink { resultLink(0) }
	}
	//firstResultLinkClickを追加
	public void firstResultLinkClick(){
		firstResultLink.click()
	}
}
  • "firstResultLink.click()"を"firstResultLinkClick()"に修正する
class GoogleWikipediaMainTest extends GebSpec {

	def "first result for wikipedia search should be wikipedia"() {
		given:
		to GoogleHomePage

		expect:
		at GoogleHomePage

		when:
                //"search.field.value"を"search"に修正
		search("wikipedia")

		then:
		waitFor { at GoogleResultsPage }

		and:
		firstResultLink.text() == "Wikipedia"

		when:
                //"firstResultLink.click()"を"firstResultLinkClick()"に修正
		firstResultLinkClick()

		then:
		waitFor { at WikipediaPage }
	}
}

4. 問題解決とリファクタリング(2)

問題

  • GoogleSearchModuleのbuttonの指定が間違っている。サンプルでは実行していないのでエラーが発生していないだけ。以下、現状のgoogle検索ボタンのHTML。inputタグは使用していない。
<button class="gbqfb" aria-label="Google Search" name="btnG" id="gbqfb">
	<span class="gbqfi gb_Sa"></span>
</button>

リファクタ手順

  1. GoogleSearchModuleの"buttonValue"を"buttonName"に変更する
  2. GoogleSearchModuleの"$("input", value: buttonValue)"を$("button", name: buttonName)"に変更する
    • inputタグがbuttonタグに変更になっていたので修正する
    • また、valueだとデザインの変更を受けやすいので、nameに変更する。nameの場合は、httpリクエストに利用されるため、変更の可能される可能性が低い
  3. GoogleSearchModuleの変更に対して、GoogleResultsPageとGoogleHomePageを修正する
  4. GoogleWikipediaMainTestを実行して動作確認

実装

  • "$("input", value: buttonValue)"を$("button", name: buttonName)"に変更する
class GoogleSearchModule extends Module {

	// a parameterised value set when the module is included
	def buttonName

	// the content DSL
	static content = {

		// name the search input control “field”, defining it with the jQuery like navigator
		field { $("input", name: "q") }

		// the search button declares that it takes us to the results page, and uses the
		// parameterised buttonName to define itself
		button(to: GoogleResultsPage) {
			$("button", name: buttonName)
		}
	}
}
  • GoogleSearchModuleの変更に対して、GoogleResultsPageとGoogleHomePageを修正する
    • search{ module GoogleSearchModule, buttonName: "btnG" }
class GoogleHomePage extends Page {

	// pages can define their location, either absolutely or relative to a base
	static url = "http://google.com/ncr"

	// “at checkers” allow verifying that the browser is at the expected page
	static at = { title == "Google" }

	static content = {
		// include the previously defined module
		search{ module GoogleSearchModule, buttonName: "btnG" }
	}

	public void search(String keyword){
		search.field.value(keyword)
	}
}


class GoogleResultsPage extends Page {
	static at = { title.endsWith "Google Search" }
	static content = {
		// reuse our previously defined module
		search{ module GoogleSearchModule, buttonName: "btnG" }

		// content definitions can compose and build from other definitions
		results { $("div.g") }
		result { i -> results[i] }
		resultLink { i -> result(i).find("a") }
		firstResultLink { resultLink(0) }
	}
	//firstResultLinkClickを追加
	public void firstResultLinkClick(){
		firstResultLink.click()
	}
}

5. 問題解決とリファクタリング(3)

問題

  • 部分一致のキーワード検索テストができない
    • 以下のようなコードにするとエラーが発生する
    • 現状のコードだと、完全一致のajax動作を前提としていることが原因
    • ここで先ほどのGoogleSearchModuleのbuttonの指定リファクタリングが生きてくる
    • 要は、検索と同時に検索ボタンをクリックしてやればよい
class GoogleWikipediaMainTest extends GebSpec {
	def "first result for wikipedia search should be wikipedia"() {
		given:
		to GoogleHomePage

		expect:
		at GoogleHomePage

		when:
		search("test")

		then:
		waitFor { at GoogleResultsPage }

		when:
		search("wikipedia")

		then:
		waitFor { at GoogleResultsPage }

		and:
		firstResultLink.text() == "Wikipedia"

		when:
		firstResultLinkClick()

		then:
		waitFor { at WikipediaPage }
	}
}

リファクタ手順

  1. GoogleResultsPageに"public void search(String keyword)"を追加する
  2. GoogleHomePage,GoogleResultsPageの"public void search(String keyword)"に"search.button.click()"を追加する
    • "wikipedia"で検索した場合は、検索結果に合致したページがあったためにajaxにより検索結果が表示された。しかし、"test"の場合は、合致したページがajaxによる検索結果が表示されないために検索ボタンを明示的に押してやる必要がある。("test"でajax結果が出た場合は、ブラウザキャッシュが残っているはずなのでクリアしてから再度、試してみてください)
  3. "waitFor { firstResultLink.text() == "Wikipedia"}"と"waitFor"を追記する
    • waitForがないと、firstResultLinkが表示される前に判定してしまい、エラーになってしまうのでそれを防ぐ
  • GoogleResultsPageに"public void search(String keyword)"を追加する
  • GoogleResultsPageの"public void search(String keyword)"に"search.button.click()"を追加する
class GoogleResultsPage extends Page {
	static at = { title.endsWith "Google Search" }
	static content = {
		// reuse our previously defined module
		search{ module GoogleSearchModule, buttonName: "btnG" }

		// content definitions can compose and build from other definitions
		results { $("div.g") }
		result { i -> results[i] }
		resultLink { i -> result(i).find("a") }
		firstResultLink { resultLink(0) }
	}
	//firstResultLinkClickを追加
	public void firstResultLinkClick(){
		firstResultLink.click()
	}
        public void search(String keyword){
		try {
			search.field.value(keyword)
			search.button.click()
		} catch (Exception e) {
		}
	}
}


  • GoogleHomePageの"public void search(String keyword)"に"search.button.click()"を追加する
class GoogleHomePage extends Page {

	// pages can define their location, either absolutely or relative to a base
	static url = "http://google.com/ncr"

	// “at checkers” allow verifying that the browser is at the expected page
	static at = { title == "Google" }

	static content = {
		// include the previously defined module
		search{ module GoogleSearchModule, buttonName: "btnG" }
	}

	public void search(String keyword){
		try {
			search.field.value(keyword)
			search.button.click()
		} catch (Exception e) {
		}
	}
}
  • "waitFor { firstResultLink.text() == "Wikipedia"}"と"waitFor"を追記する
class GoogleWikipediaMainTest extends GebSpec {
	def "first result for wikipedia search should be wikipedia"() {
		given:
		to GoogleHomePage

		expect:
		at GoogleHomePage

		when:
		search("test")

		then:
		waitFor { at GoogleResultsPage }

		when:
		search("wikipedia")

		then:
		waitFor { at GoogleResultsPage }

		and:
		waitFor { firstResultLink.text() == "Wikipedia"}

		when:
		firstResultLinkClick()

		then:
		waitFor { at WikipediaPage }
	}
}

6. まとめ

  1. Spockに記述するテスト部分には実装(Module)を表に出さないようにする。pageオブジェクトのメソッドとして定義することでそれを実現する
    • 例:searchとしては、こうすることで変更に強い実装となる
      • "問題解決とリファクタリング(3)"のような"search.button.click()"は、pageオブジェクトのメソッドにしておいたことで、テストコードを汚すことなくリファクタすることができた
  2. 処理が早くてassertでエラーが発生する場合は、waitForを追加すること
    • 例: firstResultLink.text() == "Wikipedia" → waitFor { firstResultLink.text() == "Wikipedia"}

変更点まとめ(diff)

diff --git a/src/main/groovy/module/GoogleSearchModule.groovy b/src/main/groovy/module/GoogleSearchModule.groovy
index 7ed091e..96d33b1 100644
--- a/src/main/groovy/module/GoogleSearchModule.groovy
+++ b/src/main/groovy/module/GoogleSearchModule.groovy
@@ -6,7 +6,7 @@
 class GoogleSearchModule extends Module {
 
 	// a parameterised value set when the module is included
-	def buttonValue
+	def buttonName
 
 	// the content DSL
 	static content = {
@@ -15,9 +15,9 @@
 		field { $("input", name: "q") }
 
 		// the search button declares that it takes us to the results page, and uses the
-		// parameterised buttonValue to define itself
+		// parameterised buttonName to define itself
 		button(to: GoogleResultsPage) {
-			$("input", value: buttonValue)
+			$("button", name: buttonName)
 		}
 	}
 }
\ No newline at end of file
diff --git a/src/main/groovy/page/GoogleHomePage.groovy b/src/main/groovy/page/GoogleHomePage.groovy
index 5e5fdbf..0cb547b 100644
--- a/src/main/groovy/page/GoogleHomePage.groovy
+++ b/src/main/groovy/page/GoogleHomePage.groovy
@@ -13,6 +13,14 @@
 
 	static content = {
 		// include the previously defined module
-		search { module GoogleSearchModule, buttonValue: "Google Search" }
+		search{ module GoogleSearchModule, buttonName: "btnG" }
+	}
+
+	public void search(String keyword){
+		try {
+			search.field.value(keyword)
+			search.button.click()
+		} catch (Exception e) {
+		}
 	}
 }
\ No newline at end of file
diff --git a/src/main/groovy/page/GoogleResultsPage.groovy b/src/main/groovy/page/GoogleResultsPage.groovy
index aa50b09..bd25c5d 100644
--- a/src/main/groovy/page/GoogleResultsPage.groovy
+++ b/src/main/groovy/page/GoogleResultsPage.groovy
@@ -4,15 +4,24 @@
 import module.GoogleSearchModule
 
 class GoogleResultsPage extends Page {
+	static url = "https://www.google.com/?gws_rd=ssl"
 	static at = { title.endsWith "Google Search" }
 	static content = {
 		// reuse our previously defined module
-		search { module GoogleSearchModule, buttonValue: "Search" }
+		search{ module GoogleSearchModule, buttonName: "btnG" }
+		resultLinks(wait:true){ $("li" , class:"g")}
+		firstResultLink(wait:true){ $("li" , class:"g" , 0).$("a")}
+	}
+	//firstResultLinkClickを追加
+	public void firstResultLinkClick(){
+		firstResultLink.click()
+	}
 
-		// content definitions can compose and build from other definitions
-		results { $("div.g") }
-		result { i -> results[i] }
-		resultLink { i -> result(i).find("a") }
-		firstResultLink { resultLink(0) }
+	public void search(String keyword){
+		try {
+			search.field.value(keyword)
+			search.button.click()
+		} catch (Exception e) {
+		}
 	}
 }
diff --git a/src/test/groovy/main/GoogleWikipediaMainTest.groovy b/src/test/groovy/main/GoogleWikipediaMainTest.groovy
index 1590200..12559ea 100644
--- a/src/test/groovy/main/GoogleWikipediaMainTest.groovy
+++ b/src/test/groovy/main/GoogleWikipediaMainTest.groovy
@@ -6,7 +6,6 @@
 import page.WikipediaPage
 
 class GoogleWikipediaMainTest extends GebSpec {
-
 	def "first result for wikipedia search should be wikipedia"() {
 		given:
 		to GoogleHomePage
@@ -15,16 +14,22 @@
 		at GoogleHomePage
 
 		when:
-		search.field.value("wikipedia")
+		search("test")
+
+		then:
+		waitFor { at GoogleResultsPage }
+
+		when:
+		search("wikipedia")
 
 		then:
 		waitFor { at GoogleResultsPage }
 
 		and:
-		firstResultLink.text() == "Wikipedia"
+		waitFor { firstResultLink.text() == "Wikipedia"}
 
 		when:
-		firstResultLink.click()
+		firstResultLinkClick()
 
 		then:
 		waitFor { at WikipediaPage }

【入門】Geb+SpockではじめるWebテスト~GitHub + JenkinsによるWEBテスト自動化編~

【入門】Geb+SpockではじめるWebテスト~GitHub + JenkinsによるWEBテスト自動化編~
著者:ふじさわゆうき

この記事は、以下の記事の続きです。
初めて訪問した方は以下の記事を参照してください。

目次

  1. 前回までのあらすじ
  2. 今回の目的
  3. GitHubとは
  4. Jenkinsとは
  5. GitとSourceTreeインストール
  6. GitHubへソースをアップする
  7. JenkinsインストールとGitプラグインのインストール
  8. Jenkins+GitHubでテストを自動実行する
  9. Webテストを継続的かつ自動的に実行するフロー

1. 前回までのあらすじ

  1. Gebとは何かの説明
  2. Gebのメリット説明
  3. 「Eclipse + maven + Geb + Spock」での環境構築説明
  4. サンプルプログラム実装
    1. google検索⇒検索結果⇒WIKIという流れのWebテスト実施
    2. 「Geb + Spock」による上記Webテストの実施
  5. クロスブラウザの説明
  6. スクリーンショット出力方法の説明
  7. Mavenコマンドによるテスト自動実行方法の説明

2. 今回の目的

  • "GitHub"と"Jenkins"を使ってWebテストを継続的、かつ自動的に実行する環境を構築すること
  • 全体のフローは以下の通り
  • f:id:yfj2:20141130171051p:plain

3. GitHubとは

以下、GitHub - Wikipediaより引用

  • GitHub(ギットハブ)はソフトウェア開発プロジェクトのための共有ウェブサービスである
  • Gitバージョン管理システムを使用する
  • Ruby on RailsおよびErlangで記述されており、GitHub社によって保守されている
  • 主な開発者はChris Wanstrath、PJ Hyett、Tom Preston-Wernerである
  • GitHub商用プランおよびオープンソースプロジェクト向けの無料アカウントを提供している。
  • 2009年のユーザー調査によると、GitHubは最もポピュラーなGitホスティングサイトとなった

4. Jenkinsとは

以下、Jenkins - Wikipediaより引用

  • JenkinsとはJavaで書かれたオープンソース継続的インテグレーションツールである
  • このプロジェクトはHudsonからフォークされた。オラクルが2010年12月にHudsonの商標を登録したことによるものである
  • ビルドはバージョン管理システムにおけるコミットでのトリガ、cronライクのメカニズムを通したスケジューリング、他のビルドが完了した時の構築、特定のビルドURLによるリクエストといった様々な方法で起動することができる

5. GitとSourceTreeインストール

  1. 以下のサイトからWindows用のGitをダウンロードしてインストール画面を起動する
    • 2015/05/26時点だと"Ver1.9.5"がダウンロード可能
    • Git
  2. インストール設定
    • インストール先: "C:\Program Files\Git"
    • f:id:yfj2:20141130173129p:plain
    • Adjusting your PATH environment: "Run Git from the Windows Command Prompt"
      • デフォルトはUse Git Bash onlyだが、これは使いにくいのでダメ。"Use Git from the Windows Command Prompt"を選択すること。cmd.exe、PowerShell.exe、MinGW/MSYSのbash.exeなど、他のコマンドラインシェルからもGitコマンドを使えるようになる
      • f:id:yfj2:20141130172755p:plain
  3. SourceTree for Windowsのインストール

6. GitHubへソースをアップする

  1. GitHubにユーザー登録する
    • username , email , passwordの3つを入力して"Sing Up for GitHub"をクリック。確認メールが送られてくるので認証すれば完了と簡単!
    • f:id:yfj2:20141130174730p:plain
  2. リポジトリ"GoogleWikipediaTest"を作成する。
    • f:id:yfj2:20141130175149p:plain
    • f:id:yfj2:20141130175926p:plain
  3. SourceTreeを使って"GoogleWikipediaTest"をクローンします
  4. Gitクローンしたフォルダ"C:\gitrepository\GoogleWikipediaTest"にEclipseワークスペースにある以下フォルダをコピーする
    • GoogleWikipediaTest\driver
    • GoogleWikipediaTest\src
    • GoogleWikipediaTest\pom.xml
  5. Windowsのコマンドプロンプトを立ち上げて、以下、コマンドを実行する
    • git config --global user.email "hoge@gmail.com"←GitHubに登録したメールアドレス
    • git config --global user.name "YukiFujisawa"←GitHubに登録したユーザー名
    • git add -f IEDriverServer.exe
    • git add -f choromedriver.exe
  6. SourceTreeを使ってGitHubにソースをアップ(push)する
    1. 全てのソースをステージングエリアに登録する
    2. コミットする
    3. pushする
    • f:id:yfj2:20141130182856p:plain
    • f:id:yfj2:20141130184226p:plain
  7. GitHubにアップされたことを確認する

f:id:yfj2:20141130191604p:plain

7. JenkinsインストールとGitプラグインのインストール

  1. Jenkins下記サイトからダウンロードしてインストールする
  2. Git pluginのインストール
    1. Jenkinsの管理 > プラグインの管理 > 利用可能
      • Git Client Plugin
      • Git Server Plugin
      • GitHub API Plugin
      • GitHub Plugin
    2. "ダウンロードして再起動後にインストール"をクリックする
  3. Java, Mavenの設定
    1. Jenkinsの管理 > システムの設定
      • JDK > JDK追加 > "自動インストール"のチェックを外す
        • 名前: jdk1.7.0_79
        • JAVA_HOME: C:\Program Files\Java\jdk1.7.0_79
      • Maven > Maven追加 > "自動インストール"のチェックを外す
        • 名前: maven-3.2.5
        • MAVEN_HOME: C:\apache-maven-3.2.5
    2. "保存"をクリックする
  4. Jenkinsを再起動する
    • f:id:yfj2:20141130194200p:plain

8. Jenkins+GitHubでテストを自動実行する

  1. "GoogleWikipediaTest"実行用のジョブを作成する
    1. "新規ジョブ作成"をクリック
      • ジョブ名: GoogleWikipediaTest
      • 選択: フリースタイル・プロジェクトのビルド
      • f:id:yfj2:20141130194758p:plain
    2. ジョブの設定
      • ソースコード管理 > Git
      • Repository URL: GitHubのRepositoryを指定する
      • Credentials: GitHubのユーザー名、パスワードを設定する
      • ビルド > ビルド手順の追加 > Mavenの呼び出し
        • 使用するMaven: maven-3.2.5
        • ゴール: test
  2. ジョブの実行
    • Jenkins > GoogleWikipediaTest > ビルド実行
    • f:id:yfj2:20141130202214p:plain
  3. テスト結果を確認する
    1. Jenkins > GoogleWikipediaTest > ビルド履歴 > "実行日付"をクリック
    2. "Console Output"をクリック
    3. ログの一番下にテスト結果が表示される
    • f:id:yfj2:20141130202545p:plain

9. Webテストを継続的かつ自動的に実行するフロー

Webテストを継続的かつ自動的に実行するフローは以下の通りとなる

  1. Eclipseにて「Geb + Spock」でWEBテスト作成
  2. 完成したWEBテストを"C:\gitrepository\GoogleWikipediaTest"にコピーする
  3. SourceTreeを使ってGitHubにソースをアップ(push)する
  4. Jenkins(http://localhost:8080/)にアクセスしてビルド実行
    • テストスケジュールが設定できるので、設定すれば定期実行も可能
  5. テスト結果を確認する

【入門】Geb+SpockではじめるWebテスト~Maven編~ / How to run test by Maven command using Geb+Spock?

【入門】Geb+SpockではじめるWebテスト~Maven編~ / How to run test by Maven command using Geb+Spock?
著者:ふじさわゆうき

この記事は、以下の記事の続きです。
初めて訪問した方は以下の記事を参照してください。

目次 / Table of Contents

  1. 前回までのあらすじ / Overview of up to the previous article
  2. 今回の目的 / The goal of this article
  3. Mavenのメリット / Maven benefits
  4. Mavenインストール / Maven installation
  5. Mavenコマンドによるテスト実行 / The test run by Maven command
  6. まとめ / Summary

1. 前回までのあらすじ

  1. Gebとは何かの説明 / The description of what is Geb
  2. Gebのメリット説明 / Description of Geb benefits
  3. 「Eclipse + maven + Geb + Spock」での開発環境構築説明 / Development environment construction described with using "Geb + Spock"
  4. サンプルプログラム実装 / Sample program implementation
    1. google検索⇒検索結果⇒WIKIという流れのWebテスト実施 / Web test explanation that "google search ⇒ results ⇒WIKI"
    2. 「Geb + Spock」による上記Webテストの実施 / Implementation of the above Web test using the "Geb + Spock"
  5. クロスブラウザの説明 / Description of cross-browser
  6. スクリーンショット出力方法の説明 / Description of the screen shot output method

2. 今回の目的 / The goal of this article

  • Mavenで「Geb + Spock」のWebテストを実行できるようにすること / To run web test of "Geb + Spock" Using Maven
    • Jenkinsとの連携に必須 / Required in collaboration with Jenkins

3. Mavenのメリット/ Maven benefits

以下のサイト等参照してください / Please refer to the following site, etc.

4. Mavenインストール / Maven installation

  1. Mavenをダウンロードする / Download Maven
    • Maven – Download Apache Maven
    • "apache-maven-3.3.3-bin.zip"をダウンロード / Download "apache-maven-3.3.3-bin.zip"
    • "C:\apache-maven-3.3.3"に解凍する / Unzip "apache-maven-3.3.3-bin.zip" and move to "apache-maven-3.3.3"
  2. 環境変数の設定 / Setting Environment Variables
    • システムのプロパティ > 詳細設定 > 環境変数 > Path > 編集
      • "C:\apache-maven-3.3.3\bin"を追記する / Append "C:\apache-maven-3.3.3\bin"
    • f:id:yfj2:20141129204516p:plain
    • f:id:yfj2:20141129204812p:plain
    • f:id:yfj2:20141129204818p:plain
  3. mvnコマンド実行 / mvn command execution
    • コマンドプロンプトを起動する / Start windows command prompt
    • "mvn -v"と実行する / Run "mvn -v"
    • Mavenのバージョン等が表示されたらインストール完了 / Installation is complete if Maven version confirmation
    • f:id:yfj2:20141129231155p:plain

5. MavenコマンドによるWebテスト実行 / The test run by Maven command

  1. コマンドプロンプトを起動する / Start windows command prompt
  2. GoogleWikipediaTestに移動する / Moved to GoogleWikipediaTest
    • cd [Eclipse Workspace Path]\GoogleWikipediaTest
      • (例) cd c:\gebworkspace\GoogleWikipediaTest
  3. mavenコマンドの実行 / Execution of Maven command
    • mvn test
  4. テストが起動したらOK / It is OK, if the test starts
    • f:id:yfj2:20141129233701p:plain

8. まとめ / Summary

  1. Mavenをダウンロードする / Download Maven
  2. GoogleWikipediaTestのディレクトリで"mvn test"コマンドを実行する / Run "mvn test" command in the GoogleWikipediaTest of directory
  3. テストが実行される / / It is OK, if the test starts

【Geb】ナビゲーターAPIとは? / What is Navigator API ?

【Geb】ナビゲーターAPIとは? / What is Navigator API ?
著者:ふじさわゆうき

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

Navigator APIについて公式ページの翻訳を通して理解すること
*Gebの開発環境構築については以下の記事を参照してください

目次

  1. Navigator APIとは?
  2. まとめと感想
  3. 原文情報
  4. 原文と翻訳

Navigator APIとは?

  • jQueryのような文法でコンテンツ内容を取得するAPIをGebでは、"Navigator API"と呼んでいる
  • "$"関数を使うことでCSSセレクタを基にしてコンテンツ内容を取得することができる
// CSS 3 selectors
$("div.some-class p:first[title='something']")
 
// index and/or attributeをマッチングすることで検索する
$("h1", 2, class: "heading")
$("p", name: "description")
$("ul.things li", 2)
 
//"text"は、テキスト内容要素のための特別な属性である
$("h1", text: "All about Geb")
 
// Gebが提供するmatcherや正規表現でも取得が可能
$("p", text: contains("Geb"))
$("input", value: ~/\d{3,}-\d{3,}-\d{3,}/)
 
// Chaining
$("div").find(".b")
$("div").filter(".c").parents()
$("p.c").siblings()

まとめと感想

Navigator APIを使うことで・・・

  • 様々なHTML要素を、簡単かつ柔軟に取得することができる
  • inputタグ, selectタグの値取得はもちろんのこと、値のセットも簡単におこなうことができる
  • クリックやドラック&ドロップといった動作も実行することができる
    • "dragAndDropBy($('#element'), 400, -150)"のような感じで
  • WebDriverを直節利用することもでき、より複雑な動作を実行することもできる
    • "WebElement someItem = $('li.clicky').firstElement()"のような感じで

翻訳してみて、個人的な感想としては、「Gebの価値の7割はNavigator APIである」
といえるほど、奥が深いし、すごく便利だと思いました。
以下、一例ですが、form内の要素への値検証、値セット等が非常に簡単だなーと感じています。

・HTML
<form>
    <input type="text" name="geb" value="testing" />
</form>

・Geb
//"testing"検証
$("form").geb == "testing"

//"goodness"入力
$("form").geb = "goodness"

//"goodness"検証
$("form").geb == "goodness"

Navigator APIを完全に理解し業務で使いこなせれば、非常に効率的にWeb自動テストが作成できると確信することができました。
Gebに興味がある方は、是非、実際のGebコードを書きつつ、今回の「4 Interacting with content」を参照しつつ、より効率的なWebテスト作成にお役立てください。Gebのような、素晴らしい言語が日本に広まって、テストに関わる皆様の作業効率がよりUPすることを心から願っています。

それでは、以下、「4 Interacting with content」の原文と翻訳をお楽しみください。
翻訳がおかしい箇所があれば、是非、コメントください!!より読みやすい翻訳によってGebの普及に貢献できれば嬉しいので。

原文と翻訳

目次


4 Interacting with content / コンテンツとの対話
4.1 The $ Function / $ 関数
4.1.1 CSS Selectors / CSSセレクター
4.1.2 Indexes and Ranges / インデックスと範囲
4.1.3 Attribute and Text Matching / 属性、テキストとのマッチング
4.1.3.1 Using Patterns / パターンの使用
4.1.4 Navigators are Iterable / NavigatorはIterableである
4.2 Finding & Filtering / Finding & Filtering
4.3 Traversing / トラバース(登山用語で斜面や岩壁を横(水平方向)に移動すること。)
4.4 Composition / 構成
4.5 Clicking / クリック
4.6 Determining Visibility / 可視性の決定
4.7 Size and Location / サイズと位置
4.8 Accessing tag name, attributes, text and classes / タグのname , attribute , text , classにアクセスする
4.9 Css properties / CSSプロパティ
4.10 Sending keystrokes / キーストロークを送信する
4.10.1 Non characters (e.g. delete key) / 文字以外(deleteキーのような)
4.11 Accessing input values / 入力値にアクセスする
4.12 Form Control Shortcuts / Formコントロールのショートカット
4.12.1 Setting Values / 設定値
4.12.1.1 select
4.12.1.2 multiple select
4.12.1.3 checkbox
4.12.1.4 radio
4.12.1.5 text inputs and textareas
4.12.1.6 file upload
4.13 Complex Interactions / 複雑な相互作用
4.13.1 Using the WebDriver API directly / WebDriverを直接使用する
4.13.2 Using Actions / アクションを使う
4.13.3 Using Interact Closures / インターアクトクロージャを使う
4.13.4 Interact Examples / 相互作用例
4.13.4.1 Drag and Drop / ドラック&ドロップ
4.13.4.2 Control-Clicking / Controlキーを押しながらクリック

4 Interacting with content / コンテンツとの対話

Geb provides a concise and Groovy interface to the content and controls in your browser. This is implemented through the Navigator API which is a jQuery inspired mechanism for finding, filtering and interacting with DOM elements.
Gebはブラウザ内のコンテンツやコントロールに簡潔でGroovyのインタフェースを提供する。これは、検索、フィルタリングおよびDOM要素とやりとりするためにjQueryのような仕組みのNavigator APIを介して実装されている。

4.1 The $ Function / $ 関数

The $ function is the access point to the browser’s page content. This returns a geb.navigator.Navigator object that is roughly analogous to a jQuery object. It is analogous in that it represents one or more elements on the page and can be used to refine the matched content or query the matched content. When a $ function is called that does not match any content, an “empty” navigator object is returned that represents no content. Operations on “empty” navigators return null or another “empty” navigator or other values that make sense (e.g. the size() method returns 0).
$関数は、ブラウザのページコンテンツへのアクセスポイントである。これはjQueryオブジェクトとほぼ類似してgeb.navigator.Navigatorオブジェクトを返す。
ページ上の1つ以上の元素を表し、マッチしたコンテンツを絞り込むか、一致した内容を照会するために使用することができるという点では類似している。
$関数は、任意のコンテンツと一致しないことが呼び出されたときに、「empty」のNavigator Objectはそれが全く内容を返す。
「empty」のナビゲーター上の操作は、nullを返すか、意味をなさない別の「empty」のNavigatorまたは他の値となる(例えば、size()メソッドは0を返す)。

The signature of the $ function is as follows
$関数のシグネチャは次のようになる

$(≪css selector≫, ≪index or range≫, ≪attribute / text matchers≫)

The following is a concrete example
以下、具体例である

$("h1", 2, class: "heading")

This would find the 3rd h1 element whose class attribute is exactly “heading”.
上記具体例は、class属性が正確に「heading」されている3番目のH1要素を検索することができる

All arguments are optional, meaning the following calls are all valid:
すべての引数は、次の呼び出しではすべてのオプションは有効である

$("div p", 0)
$("div p", title: "something")
$(0)
$(title: "something")

4.1.1 CSS Selectors / CSSセレクター

You can use any CSS selector that the underlying WebDriver supports…
WebDriverがサポートしているCSSセレクタを使用することができる

$("div.some-class p:first[title='something']")

In the case of the HTMLUnit driver, which does not support CSS selectors at all, only basic CSS 2 type selectors can be used. A future version of the HTMLUnit driver may gain better CSS selector support.
HTMLUnitドライバは、全くCSSセレクタをサポートしていない。基本的なCSS 2タイプのセレクタのみ使用することができる。 HTMLUnitドライバの将来のバージョンでは、より良いCSSセレクタのサポートを得られるかもしれない

4.1.2 Indexes and Ranges / インデックスと範囲

When matching, a single positive integer or integer range can be given to restrict by index.
マッチングの際に、単一の正の整数または整数範囲はインデックスによって制限することができる。

Consider the following html…
次のHTMLを検討してみよう

<p>a</p>
<p>b</p>
<p>c</p>

We can use indexes to match content like so.
私たちはそうのようなコンテンツに一致するようにインデックスを使用することができる。

$("p", 0).text() == "a"
$("p", 2).text() == "c"
$("p", 0..1)*.text() = ["a", "b"]
$("p", 1..2)*.text() = ["b", "c"]

See below for an explanation of the text() method and the use of the spread operator.
text()メソッドの説明とpread operatorの使用については、以下を参照すること

4.1.3 Attribute and Text Matching / 属性、テキストとのマッチング

Matches can be made on attributes and node text values via Groovy’s named parameter syntax. The value text is treated specially as a match against the node’s text. All other values are matched against their corresponding attribute values.
Matches
Groovyでパラメータ構文と呼ばれる属性とノードテキスト値で一致させるができる。テキスト値は、ノードテキストに対する一致として特別に扱われている。他のすべての値は、対応する属性値と一致する

Consider the following html
次のHTMLを検討してみよう

<p attr1="a" attr2="b">p1</p>
<p attr1="a" attr2="c">p2</p>

We can use attribute matchers like so…
次のように属性マッチを使用することができる

$("p", attr1: "a").size() == 2
$("p", attr2: "c").size() == 1

Attribute values are anded together
属性値がAND抽出される

$("p", attr1: "a", attr2: "b").size() == 1

We can use text matchers like so…
次のようにテキストマッチを使うことができる

$("p", text: "p1").size() == 1

4.1.3.1 Using Patterns / パターンの使用

To match the entire value of an attribute or the text you use a String value. It is also possible to use a Pattern to do regexp matching…
属性値またはテキストと完全一致することで、String値を使用することができる。正規表現マッチングを行うためにパターンを使用することも可能である

$("p", text: ~/p./).size() == 2

Geb also ships with a bunch of shortcut pattern methods
Gebのショートカットパターンメソッドを利用することができる

$("p", text: startsWith("p")).size() == 2
$("p", text: endsWith("2")).size() == 1

The following is the complete listing:
以下、リストである

Case Sensitive Case Insensitive Description
startsWith iStartsWith 指定した値で始まる値と一致する
contains iContains 指定した値を含む値と一致する
endsWith iEndsWith 指定した値で終わる値と一致する
containsWord iContainsWord 指定した値が空白に囲まれていても、指定した値を含む値と一致する
notStartsWith iNotStartsWith 指定した値で始まらない値と一致する
notContains iNotContains 指定した値が何も含まない値と一致する
notEndsWith iNotEndsWith 指定した値で終わらない値と一致する
notContainsWord iNotContainsWord 空白や先頭または末尾に囲まれた値が含まれていない値に一致する

All of these methods themselves can take a String or a Pattern
これら全てのメソッドは、それ自体が文字列またはパターンを取ることができる

$("p", text: contains(~/\d/)).size() == 2

4.1.4 Navigators are Iterable / ナビゲーターはIterable

The navigator objects implement the Java Iterable interface, which allows you to do lots of Groovy stuff like use the max() function
Navigator objectを使用すると、Java Iterable interfaceを実装することができる。Java Iterable interfaceは、max()関数のような多くのGroovy機能を利用することもできる。

<p>1</p>
<p>2</p>
$("p").max { it.text() }.text() == "2"

This also means that navigator objects work with the Groovy spread operator
これはまた、Navigator objectがGroovyのspread operatorで動作することを意味する

$("p")*.text().max() == "2"

4.2 Finding & Filtering / Finding & Filtering

Navigator objects have find and $ methods for finding descendants,
filter and not methods for reducing the matched content.
Navigator objectには、findメソッドと$メソッドがある。

Consider the following HTML
次のHTMLを検討してみよう

<div class="a">
    <p class="b">geb</p>
</div>
<div class="b">
    <input type="text"/>
</div>

We can select p.b by
p.bを以下のようにセレクトすることができる

$("div").find(".b")
$("div").$(".b")

We can select div.b by
div.bを以下のようにセレクトすることができる

$("div").filter(".b")

or
または

$(".b").not("p")

We can select the div containing the p with…
pを含むdivというセレクトもできる

$("div").has("p")

Or select the div containing the input with a type attribute of “text” like so…
属性が"text"のinputタグを含んだdivでもセレクトできる

$("div").has("input", type: "text")

The find and $ methods support the exact same argument types as the $ function.
findメソッドと$メソッドは同じ引数の型を提供する。$関数として。

The filter, not and has methods have the same signatures - they accept: a selector string, a predicates map or both.
フィルター, "not" "and" は同様のシグネチャをもつ。

These methods return a new navigator object that represents the new content.
これらのメソッドは、新しいコンテンツを表す新しいNavigator objectを返す

4.3 Traversing / トラバース(登山用語で斜面や岩壁を横(水平方向)に移動すること。)

Navigators also have methods for selecting content around the matched content.
Navigatorは、マッチしたコンテンツ周りのコンテンツを選択するためのメソッドも実装している

Consider the following HTML…
次のHTMLを検討してみよう

<div class="a">
    <div class="b">
        <p class="c"></p>
        <p class="d"></p>
        <p class="e"></p>
    </div>
    <div class="f"></div>
</div>

You can select content around p.d by…
次のように"p.d"周りを選択できる

$("p.d").previous() // 'p.c'
$("p.e").prevAll() // 'p.c' & 'p.d'
$("p.d").next() // 'p.e'
$("p.c").nextAll() // 'p.d' & 'p.e'
$("p.d").parent() // 'div.b'
$("p.c").siblings() // 'p.d' & 'p.e'
$("div.a").children() // 'div.b' & 'div.f'

Consider the following HTML…
次のHTMLを検討してみよう

<p class="a"></p>
<p class="b"></p>
<p class="c"></p>

The following code will select p.b & p.c
次のコードで"p.b"と"p.c"を選択できるだろう

$("p").next()

The previous, prevAll, next, nextAll, parent, parents, closest, siblings and children methods can also take CSS selectors and attribute matchers.
次のメソッドも、CSSセレクタ、属性値とマッチする。"previous", "prevAll", "next", "nextAll", "parent", "closest", "siblings", "children"

Using the same html, the following examples will select p.c…
上記と同様のHTMLを使って"p.c"を選択する例を挙げてみよう

$("p").next(".c")
$("p").next(class: "c")
$("p").next("p", class: "c")

Likewise, consider the following HTML
次のHTMLも検討してみよう

<div class="a">
    <div class="b">
        <p></p>
    </div>
</div>

The following examples will select div.b
次は、"div.b"を選択する例である

$("p").parent(".b")
$("p").parent(class: "b")
$("p").parent("div", class: "b")

The closest method is a special case in that it will select the first ancestors of the current elements that match a selector. There is no no-argument version of the closest method.
closestメソッドは、特別な場合に利用する。その場合とは、セレクタとマッチした現在要素の一番親を選択する場合である。closestメソッドには、引数無しバージョンは存在しない。

these will select div.a…
以下、"div.a"が選択されるだろう

$("p").closest(".a")
$("p").closest(class: "a")
$("p").closest("div", class: "a")

The nextUntil, prevUntil and parentsUntil methods return all nodes along the relevant axis until the first one that matches a selector or attributes. Consider the following markup:
"nextUntil", "prevUntil", "parentsUntil"のメソッドは、セレクタや属性に一致する最初の一つまで、関連する軸に沿ってすべてのノードを返す。

<div class="a"></div>
<div class="b"></div>
<div class="c"></div>
<div class="d"></div>

The following examples will select div.b and div.c
次の例では、"div.b"と"div.c"が選択されるだろう

$(".a").nextUntil(".d")
$(".a").nextUntil(class: "d")
$(".a").nextUntil("div", class: "d")

4.4 Composition / 構成

It is also possible to compose navigator objects from other navigator objects, for situations where you can’t express a content set in one query. To do this, simply call the $ function with the navigators to use…
一つのクエリでコンテンツのセットを表現できない状況のために、他のnavigator objectからnavigator objectを構成することができる
これを行うには、単に使用するためにnavigatorと$関数を呼び出す

$($("div.a"), $("div.d"))

This will return a new navigator object that represents only the a and d divs.
上記は、"div.a"と"div.d"を含んだnavigatorが返されるだろう

You can compose navigator objects from content. So given a page content definition:
コンテンツからnavigotor objectを構成することができる。なので、ページ内容定義はすることができる

static content = {
    divElement { divClass -> $('p', 'class': divClass) }
}
$(divElement('a'), divElement('d'))

You will get a navigator that contains the same elements as the one above.
上記のものと同じ要素が含まれているnavigatorを取得するだろう

4.5 Clicking / クリック

Navigator objects implement the click() method, which will instruct the browser to click only the first item the navigator has matched.
Navigator objectは、click()メソッドを実装している。それは、最初にマッチしたnavigator要素のブラウザクリックとなる。

There are also click(Class) and click(List) methods that are analogous to the browser object’s page(Class) and page(List) methods respectively.
This allow page changes to be specified at the same time as click actions.
"click(Class)"とclick(List)メソッドは、各ブラウザの"page(Class)" と "page(List)" メソッドに似ている
これらのメソッドにより、ページの変更をクリック・アクションと同時に指定することができる

For example
例えば、

$("input.loginButton").click(LoginPage)

Would click the “input.loginButton” element, then effectively call browser.page(LoginPage) and verify that the browser is at the expected page.
「input.loginButton」要素をクリックすることで、browser.page(LoginPage)を効果的に呼び出し、ブラウザが予期したページであることを確認することができる

All of the page classes passed in when using the list variant have to have an “at” checker defined, otherwise an UndefinedAtCheckerException will be thrown.
全てのページクラスは、"at"チェッカーに定義した検証リストに合格しなければならない。さもないと"UndefinedAtCheckerException"例外がスローされる。

4.6 Determining Visibility / 可視性の決定

Navigator objects have a displayed property that indicates whether the element is visible to the user or not. The displayed property of a navigator object that doesn’t match anything is always false
Navigator objectは、要素がユーザかに表示されるかどうかを示す表示された特性を有している。何も一致しないnavigatorオブジェクトの表示されたプロパティは常にfalseとなる

4.7 Size and Location / サイズと位置

You can obtain the size and location of content on the page. 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 content.
そのページ上のコンテンツのサイズと位置を取得することができる。すべてのユニットはピクセル単位で指定する。位置はコンテンツのページ(または親フレーム)の左上からの距離を表し、xとyプロパティとして利用可能である。サイズは、高さと幅のプロパティを介して利用可能である。

All of these properties operate on the first matched element only.
以下に示すプロパティはすべて、最初にマッチした要素上でのみ動作する

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

To obtain any of the properties for all matched elements, you can use the Groovy spread operator.
すべてのマッチした要素のプロパティのいずれかを取得するには、Groovyのspread operatorを使用することができる

$("div")*.height == [20, 30]
$("div")*.width == [40, 50]
$("div")*.x == [60, 70]
$("div")*.y == [80, 90]

4.8 Accessing tag name, attributes, text and classes / タグのname , attribute , text , classにアクセスする

The tag(), text(), @attribute and classes() methods return the requested content on the first matched content.
The classes() method returns a java.util.List of unique class names sorted alphabetically.

tag(), text(), @attributeおよびclasses()メソッドは、最初にマッチしたコンテンツに対する要求されたコンテンツを返す。
classes()メソッドは、アルファベット順にソートユニークなクラス名のjava.util.Listを返す。

Consider the following HTML…
次のHTMLを検討してみよう

<p title="a" class="a para">a</p>
<p title="b" class="b para">b</p>
<p title="c" class="c para">c</p>

The following assertions are valid
次のアサーションによって検証する

$("p").text() == "a"
$("p").tag() == "p"
$("p").@title == "a"
$("p").classes() == ["a", "para"]

To obtain information about all matched content, you use the Groovy spread operator
すべてのマッチしたコンテンツに関する情報を取得するには、Groovyのspread operatorを使用する

$("p")*.text() == ["a", "b", "c"]
$("p")*.tag() == ["p", "p", "p"]
$("p")*.@title == ["a", "b", "c"]
$("p")*.classes() == [["a", "para"], ["b", "para"], ["c", "para"]]

4.9 Css properties / Cssプロパティ

Css properties of a navigator can be accessed using the css() method.
ナビゲーターのCSSプロパティは、css()メソッドを使用してアクセスすることができる

Consider the following HTML…
次のHTMLを検討してみよう

<div style="float: left">text</div>

You can obtain value of the float css property in the following way
次のようにfloat cssプロパティの値を取得することができる

$("div").css("float") == "left"

There are some limitations when it comes to retrieving css properties of Navigator objects.
Color values should be returned as rgba strings, so, for example if the background-color property is set as green in the HTML source, the returned value will be rgba(0, 255, 0, 1).
Note that shorthand CSS properties (e.g. background, font, border, border-top, margin, margin-top, padding, padding-top, list-style, outline, pause, cue) are not returned, in accordance with the DOM CSS2 specification -
you should directly access the longhand properties (e.g. background-color) to access the desired values.
Navigator objectのCSSプロパティを取得する場合、いくつかの制限がある。
例えば、背景色プロパティがHTMLソースに"green"と設定されている場合、色の値はRGBAの文字列を返さない。返される値はRGBAとなる(0, 255, 0 , 1)
そのshorthand CSSプロパティに注意すること(例:background, font, border, border-top, margin, margin-top, padding, padding-top, list-style, outline, pause, cue)は、DOM CSS2仕様に従って、返されません -
直接、取りたい値にアクセスするためは、longhand特性(例えば、背景色)にアクセスする必要がある。

4.10 Sending keystrokes / キーストロークを送信する

Keystrokes can be sent to any content via the leftShift operator, which is a shortcut for the sendKeys() method of WebDriver.
WebDriverのためのsendKeys()を使って、leftShift操作をどのコンテンツにも送信することができる

("div") << "abc"

4.10.1 Non characters (e.g. delete key) / 文字以外(例えばdeleteキー)

It is possible to send non-textual characters to content by using the WebDriver Keys enumeration.
WebDriverのキー定数を使うことによって、コンテンツに文字以外のキーを送信することができる

import org.openqa.selenium.Keys
 
$("input", name: "firstName") << Keys.chord(Keys.CONTROL, "c")

4.11 Accessing input values / 入力値にアクセスする

The value of input, select and textarea elements can be retrieved and set with the value method.
"input", "select", "textarea"の要素には、valueメソッドを使うことで取得、設定することができる

Calling value() with no arguments will return the String value of the first element in the Navigator.
value()を引数無しで呼び出すことによって、Navigatorの最初の要素のStringを取得することができる

Calling value(value) will set the current value of all elements in the Navigator.
value(value)メソッドを呼び出すことによって、Navigatorのすべての値に値をセットすることができる

The argument can be of any type and will be coerced to a String if necessary.
引数は、任意のタイプのものであってもよいし、必要に応じて文字列に強制してもよい。

The exceptions are that when setting a checkbox value the method expects a boolean (or, an existing checkbox value) and when setting a multiple select the method expects an array or Collection of values.
例外は、メソッドがbooleanを期待するcheckboxの値を設定する際(または、既存のcheckboxの値)を、複数の方法を選択設定することである。その際には値の配列またはCollectionを想定していることである。

4.12 Form Control Shortcuts / Formコントロールのショートカット

Interacting with form controls (input, select etc.) is such a common task in web functional testing that Geb provides convenient shortcuts for common functions.
"input"、"select"などフォームコントロールとのやり取りには、Web機能テストのような共通タスクを提供する。Gebは、共通機能として便利なショートカットを提供する。

Geb supports the following shortcuts for dealing with form controls.
Gebは、フォームコントロールショートカットをサポートしている

Consider the following HTML…
次のHTMLを検討してみよう

<form>
    <input type="text" name="geb" value="testing" />
</form>

The value can be read and written via property notation…
プロパティを介して、値の読み込みと書き込みができる

$("form").geb == "testing"
$("form").geb = "goodness"
$("form").geb == "goodness"

These are literally shortcuts for…
上記、記法は、次の記法のショートカットである

$("form").find("input", name: "geb").value() == "testing"
$("form").find("input", name: "geb").value("goodness")
$("form").find("input", name: "geb").value() == "goodness"

There is also a shortcut for obtaining a navigator based on a control name
コントロール"name"に基づいて、navigatorを取得するショートカットもある

$("form").geb()

Which is literally a shortcut for…
上記、記法は以下記法のショートカットである

$("form").find("input", name: "geb")

If your content definition (either a page or a module) describes content which is an input, select or textarea, you can access and set its value the same way as described above for forms. Given a page and module definitions for the above mentioned HTML:
あなたのコンテンツ定義(ページまたはモジュールのどちらか)が"input"、"select"または"textarea"で内容を記述した場合は、上記、フォームコントロールと同様に値をセットしたり、取得したりすることができる。上記のHTMLのページとモジュール定義を考えてみよう

class ShortcutModule extends Module {
    static content = {
        geb { $('form').geb() }
    }
}
 
static content = {
    geb { $('form').geb() }
    shortcutModule { module ShortcutModule }
}

The following will pass
次のようになる

assert geb == "testing"
geb = "goodness"
assert geb == "goodness"

As well as:
同様に

assert shortcutModule.geb == "testing"
shortcutModule.geb = "goodness"
assert shortcutModule.geb == "goodness"

The following examples describe usage of form controls only using code like "$("form").someInput".
次の例では、"$("form").someInput"のようなコードを使ってフォームコントロールを説明する

4.12.1 Setting Values / 設定値

Trying to set a value on an element which is not one of input, select or textarea will cause an UnableToSetElementException to be thrown.
"input" , "select" , "textarea"のどれでもないelementに値を設定しようとするとUnableToSetElementExceptionがスローされる

4.12.1.1 select

Select values are set by assigning the value or text of the required option.
Assigned values are automatically coerced to String. For example…
必須オプションである"value" , "option" , "test"のいづれかを指定することで選択値をセットすることができる。
セットした値は、自動的にStringに強制変換される

<select name="artist">
    <option value="1">Ima Robot</option>
    <option value="2">Edward Sharpe and the Magnetic Zeros</option>
    <option value="3">Alexander</option>
</select>

We can select options with
次のようにオプションを選択できる

$("form").artist = "1"         // first option selected by its value attribute
$("form").artist = 2           // second option selected by its value attribute
$("form").artist = "Ima Robot" // first option selected by its text

If you attempt to set a select to a value that does not match the value or text of any options, an IllegalArgumentException will be thrown.
任意のオプションの値またはテキストと一致しない値を選択して設定した場合、IllegalArgumentExceptionがスローされる

4.12.1.2 multiple select

If the select has the multiple attribute it is set with a array or Collection of values.
Any options not in the values are un-selected. For example…
"select"は、複数属性の場合、"array" または "Collection"をセットする
どの値も選択しない場合の値は、"un-selected"である。例を挙げる。

 <select name="genres" multiple>
    <option value="1">Alt folk</option>
    <option value="2">Chiptunes</option>
    <option value="3">Electroclash</option>
    <option value="4">G-Funk</option>
    <option value="5">Hair metal</option>
</select>

We can select options with…
次のようにオプションを選択することができる

$("form").genres = ["2", "3"]                 // second and third options selected by their value attributes
$("form").genres = [1, 4, 5]                  // first, fourth and fifth options selected by their value attributes
$("form").genres = ["Alt folk", "Hair metal"] // first and last options selected by their text
$("form").genres = []                         // all options un-selected

If the collection being assigned contains a value that does not match the value or text of any options, an IllegalArgumentException will be thrown.
指定したcollectionが"value", "text" ,"options"のどれにもマッチしない場合は、IllegalArgumentExceptionがスローされる

4.12.1.3 checkbox

Checkboxes are generally checked/unchecked by setting their value to true or false.
"Checkbox"は、値に"true"または"false"を設定することで"選択済み/未選択"を設定することが通常できる。

You can also check a checkbox by explicitly setting its value.
This is useful when you have a number of checkboxes with the same name.
明示的にその値を設定することで、checkboxを選択することもできる。
同じnameのcheckboxが複数ある場合に便利だろう。その例を示す

<input type="checkbox" name="pet" value="dogs" />
<input type="checkbox" name="pet" value="cats" />


You can select dogs as your pet type, as follows:
次のようにすることでname="pet"の"dogs"を選択することができる

$("input", type: "checkbox", name: "pet").value("dogs")

Calling value() on a checked checkbox will return the value of its value attribute, i.e:
選択済みのcheckboxでvalue()を呼び出すとその属性の値を返す。次に例を示す

・html
<input type="checkbox" name="pet" value="dogs" checked="checked"/>

・Geb
assert $("input", type: "checkbox", name: "pet").value() == "dogs"

Calling value() on an unchecked checkbox will return false, i.e:
未選択のcheckboxでvalue()を呼び出すと、falseが返される。次に例を示す

・html
<input type="checkbox" name="pet" value="dogs"/>
 
・Geb
assert $("input", type: "checkbox", name: "pet").value() == false

In general you should use Groovy Truth when checking if a checkbox is checked:
通常、checkboxが選択済みであれば、Groovy Truthを使うべきだろう

if ($("input", type: "checkbox", name: "pet").value()) {
    //checkboxの"pet"が選択済みの場合のみ処理が実行される
}

4.12.1.4 radio

Radio values are set by assigning the value of the radio button that is to be selected or the label text associated with a radio button.
ラジオボタンに関連するラベルテキスト、または"selected" によってRadioボタンの値をセットすることができる

For example, with the following radio buttons…
次にラジオボタンの例を示す

<label for="site-current">Search this site</label>
<input type="radio" id="site-current" name="site" value="current">
 
<label>Search Google
    <input type="radio" name="site" value="google">
</label>

We can select the radios with
次のようにラジオボタンを選択することができる

$("form").site = "current"          // selects the first radio by its value
$("form").site = "Search this site" // selects the first radio by its label
$("form").site = "Search Google"    // selects the second radio by its label

4.12.1.5 text inputs and textareas

In the case of a text input, the assigned value becomes the input’s value attribute and for a textarea effectively becomes the text.
テキスト入力の場合、割り当てられた値は、inputのvalue属性になり、TextAreaの効果的にテキストになる。

It is also possible to append text by using the send keys shorthand…
ショートカットキーを使うことで、テキスト追加することができる

・html
<input name="language" value="gro" />

・Geb
$("form").language() << "ovy"
assert $("form").language == "groovy"

Which an also be used for non-character keys…
非文字キーを使うこともできる

・html
<input name="postcode" />
 
・Geb
import org.openqa.selenium.Keys
 
$("form").postcode = "12345"
$("form").postcode() << Keys.BACK_SPACE
assert $("form").postcode == "1234"

4.12.1.6 file upload

It’s currently not possible with WebDriver to simulate the process of a user clicking on a file upload control and choosing a file to upload via the normal file chooser.
However, you can directly set the value of the upload control to the absolute path of a file on the system where the driver is running and on form submission that file will be uploaded.
ユーザーがファイルアップロードコントロールをクリックして、アップロードするファイルを選択することは現在はできません。なぜならWebDriverで出来ないから。
ただし、システム上ファイルの絶対パスをアップロード・コントロールの値に設定することで直接ファイルをアップロードすることはできる。

<input type="file" name="csvFile">
 
$("form").csvFile = "/path/to/my/file.csv"

4.13 Complex Interactions / 複雑な相互作用

WebDriver supports interactions that are more complex than simply clicking or typing into items, such as dragging.
You can use this API from Geb, or use the more Geb friendly interact {} DSL (explained below).
WebDriverはより複雑でよりシンプルなクリック、タイピング、ドラックをサポートしてくれるだろう。
GebのAPIまたは、Gebの簡単な相互作用{}DSLを(後述)を使用することができる。

4.13.1 Using the WebDriver API directly / WebDriverを直接使用する

A Geb navigator object is built on top of a collection of WebDriver WebElement objects.
It is possible to access the contained WebElements via the following methods on navigator objects:

Geb navigator objectはWebDriver WebElement objectのコレクションの上に構築されている
navigator objectのメソッドを経由してWebDriverに含まれているWebElementsにアクセスすることが可能である。

WebElement firstElement()
WebElement lastElement()
Collection<WebElement> allElements()

By using the methods of the WebDriver Actions class with WebElements, complex user gestures can be emulated.
WebElementsと一緒にしてWebDriver Actions classWebDriverアクションクラスのメソッドを使用することにより、複雑なユーザのジェスチャーをエミュレートすることができる。

4.13.2 Using Actions / アクションを使う

Create an Actions instance after obtaining the WebDriver driver:
"WebDriver driver"を取得した後にActionインスタンスを作成する

def actions = new Actions(driver)

Next, use methods of Actions to compose a series of UI actions, then call build to create a concrete Action:
次に、一連のUIコンポーネントされたアクションメソッドを利用することで、具体的なアクションを作成するためにビルドする

import org.openqa.selenium.Keys
 
WebElement someItem = $('li.clicky').firstElement()
def shiftDoubleClickAction = actions.keyDown(Keys.SHIFT).doubleClick(someItem).keyUp(Keys.SHIFT).build()

Finally, call "perform()" to actually trigger the desired mouse or keyboard behavior:
最後に、実際に必要なマウスやキーボードの動作をトリガーするために"perform()"を呼び出す、

shiftDoubleClickAction.perform()

4.13.3 Using Interact Closures / インターアクトクロージャを使う

To cut down on the amount of typing required, use an interact closure instead of using class Actions explicitly.
必要なタイピングの量を削減するには、明示的にクラスアクションを使用する代わりにinteract closureを使用します

When using an interact closure, an Actions instance is implicitly created, built into an Action, and performed.
interact closureを使用する場合は、Actionのインスタンスが暗黙のうちに、作成したアクションに組み込まれ、そして実行される

As an added bonus, Geb navigators can be passed directly to Actions methods within an interact closure.
追加ボーナスとして、Geb navigatorは、interact closure内では、Actionメソッドに直接渡すことができる

This interact closure performs the same work as the calls in the ‘Using Actions’ section:
interact closureは、‘Using Actions’と同等に動作する

import org.openqa.selenium.Keys
 
interact {
    keyDown(Keys.SHIFT)
    doubleClick($('li.clicky'))
    keyUp(Keys.SHIFT)
}

This method creates code that is more readable than using Actions directly.
このメソッドを使うことで、Actionを直接使用するよりも読みやすいコードを作成することができる

For the full list of available interactions, see the documentation for the WebDriver Actions class.
利用可能な相互作用の完全なリストについては、WebDriver Actions classのドキュメントを参照してください。
http://selenium.googlecode.com/svn/trunk/docs/api/java/org/openqa/selenium/interactions/Actions.html

4.13.4 Interact Examples / 相互作用例

Interact closures (or Actions) can be used to perform behaviors that are more complicated than clicking buttons and anchors or typing in input fields.
Interact closure(またはAction)を使うことで、ボタンとanchorをクリックする、またはinputフィールドにタイプするなどより複雑な動作を実行することができる

Shift-double-clicking was demonstrated earlier.
Shiftキーを押しながらダブルクリックできることが、以前に実証された。

4.13.4.1 Drag and Drop / ドラック&ドロップ

You can drag and drop an element on the page by using clickAndHold, moveByOffset, and then release.

clickAndHold, moveByOffset, and then release drag and drop an element on the page.
clickAndHold, moveByOffset, そしてreleaseを使うことで、そのページの要素をドラック&ドロップすることができる

interact {
    clickAndHold($('#element'))
    moveByOffset(400, -150)
    release()
}

Drag-and-dropping can also be accomplished using the dragAndDropBy convenience method from the Actions API:
Drag-and-droppingもAction APIからdragAndDropByのような便利なメソッドを用いて達成することができる。

interact {
    dragAndDropBy($('#element'), 400, -150)
}

In this particular example, the element will be clicked then dragged 400 pixels to the right and 150 pixels upward before being released.
上記の例では、ある要素をクリックして、右に400 pixels 上に150 pixels 移動してリリースする

4.13.4.2 Control-Clicking / Controlキーを押しながらクリック

Control-clicking several elements, such as items in a list, is performed the same way as shift-clicking.
クリックをコントロールするいくつかの要素(listのなかのitemsのような)は、shift-clickingと同様のやり方で動作する

import org.openqa.selenium.Keys
 
interact {
    keyDown(Keys.CONTROL)
    click($('ul.multiselect li', text: 'Order 1'))
    click($('ul.multiselect li', text: 'Order 2'))
    click($('ul.multiselect li', text: 'Order 3'))
    keyUp(Keys.CONTROL)
}

【入門】Geb+SpockではじめるWebテスト~スクリーンショット編~

【入門】Geb+SpockではじめるWebテスト~スクリーンショット編~
著者:ふじさわゆうき

この記事は、以下の記事の続きです。
初めて訪問した方は以下の記事を参照してください。

目次

  1. 前回までのあらすじ
  2. 今回の目的
  3. GebConfig.groovyの修正
  4. 継承クラスの変更(GebSpec→GebReportingSpec)
  5. まとめ

1. 前回までのあらすじ

  1. Gebとは何かの説明
  2. Gebのメリット説明
  3. 「Eclipse + maven + Geb + Spock」での環境構築説明
  4. サンプルプログラム実装
    1. google検索⇒検索結果⇒WIKIという流れのWebテスト実施
    2. 「Geb + Spock」による上記Webテストの実施
  5. クロスブラウザの説明
    1. 【入門】Geb+SpockではじめるWebテスト~クロスブラウザテスト編~ - yfj2’s Automatic Web Test Related Blog

2. 今回の目的

  • 「Geb + Spock」のWebテストで、テスト失敗時のスクリーンショットを出力できるようにすること

3. GebConfig.groovyの修正

  1. Geb用の設定ファイルGebConfig.groovyに"reportsDir"と"reportOnTestFailureOnly"を追加する
    • reportsDir = "target/geb-reports"
      • レポート出力先(スクリーンショット出力先)を"target/geb-reports"に設定する
    • reportOnTestFailureOnly = true
      • "true"だと、テストが失敗した場合のみスクリーンショットが出力される。"false"にすると成功した場合でもスクリーンショットが出力される
//choose "htmlunit", "firefox", "ie", "chrome"
driver = "firefox"

//reports setting
reportsDir = "target/geb-reports"
reportOnTestFailureOnly = true

//chrome - http://chromedriver.storage.googleapis.com/index.html
System.setProperty("webdriver.chrome.driver", "driver/chromedriver.exe")

//ie - http://selenium-release.storage.googleapis.com/index.html
System.setProperty("webdriver.ie.driver", "driver/IEDriverServer.exe")

4. 継承クラスの変更(GebSpec→GebReportingSpec)

  1. GoogleWikipediaMainTest.groovyを開く
    • GoogleWikipediaTest/src/test/java/main/GoogleWikipediaMainTest.groovy
  2. "extends GebSpec"を"extends GebReportingSpec"に修正する
  3. "import geb.spock.GebSpec"を"import geb.spock.GebReportingSpec"に修正する
  4. GoogleWikipediaMainTest.groovyのテストが失敗するように修正する。"wikipedia"→"wikipedi"
    • firstResultLink.text() == "Wikipedi"
package main

import geb.spock.GebReportingSpec
import page.GoogleHomePage
import page.GoogleResultsPage
import page.WikipediaPage

class GoogleWikipediaMainTest extends GebReportingSpec {

	def "first result for wikipedia search should be wikipedia"() {
		given:
		to GoogleHomePage

		expect:
		at GoogleHomePage

		when:
		search.field.value("wikipedia")

		then:
		waitFor { at GoogleResultsPage }

		and:
		firstResultLink.text() == "Wikipedi"

		when:
		firstResultLink.click()

		then:
		waitFor { at WikipediaPage }
	}
}

5. GoogleWikipediaMainTest.groovyの右クリック > 実行 > JUnitテスト
6. "target/geb-reports/main/GoogleWikipediaMainTest"以下に失敗時のスクリーンショットが出力される

  • 失敗時のスクリーンショット

f:id:yfj2:20141115200804p:plain

  • Spockのエラーログ
Condition not satisfied:

firstResultLink.text() == "Wikipedi"
|               |      |
|               |      false
|               |      1 difference (88% similarity)
|               |      Wikipedi(a)
|               |      Wikipedi(-)
|               Wikipedia

5. まとめ

  1. Geb用の設定ファイルGebConfig.groovyに"reportsDir"と"reportOnTestFailureOnly"を追加する
    • reportsDir = "target/geb-reports"
    • reportOnTestFailureOnly = true
  2. 継承する(extends)クラスを"GebSpec"から"GebReportingSpec"に変更する
  3. テストが失敗すると"target/geb-reports"以下にスクリーンショットが出力される

【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

【入門】Geb+SpockではじめるWebテスト~クロスブラウザテスト編~ / Setting up and running of the cross-browser test

【入門】Geb+SpockではじめるWebテスト~クロスブラウザテスト編~ / Setting up and running of the cross-browser test

著者:ふじさわゆうき
Author: Yuki Fujisawa

この記事は、以下の記事の続きです。/ This article is a continuation of the following article.
初めて訪問した方は以下の記事を参照してください。 / First person who visited, please see the following article.

目次 / Table of contents

  1. 前回までのあらすじ / Overview of up to the previous article
  2. 今回の目的 / The goal of this article
  3. ChromeによるWebテスト / Web testing with Chrome
  4. IEによるWebテスト / Web testing with IE
  5. FireFoxによるWebテスト / Web testing with FireFox
  6. まとめ

1. 前回までのあらすじ

  1. Gebとは何かの説明 / The description of what is Geb
  2. Gebのメリット説明 / Description of Geb benefits
  3. 「Eclipse + maven + Geb + Spock」での開発環境構築説明 / Development environment construction described with using "Geb + Spock"
  4. サンプルプログラム実装 / Sample program implementation
    1. google検索⇒検索結果⇒WIKIという流れのWebテスト実施 / Web test explanation that "google search ⇒ results ⇒WIKI"
    2. 「Geb + Spock」による上記Webテストの実施 / Implementation of the above Web test using the "Geb + Spock"

2. 今回の目的 / The purpose of this article

  • クロスブラウザテストができるようになること / To get ability cross-browser testing with "Geb and Spock".
    • 対象はChrome , InternetExplorer(IE) , FireFoxの3ブラウザ / Taget browser are Chrome , InternetExplorer(IE) and FireFox

3. ChromeによるWebテスト / Web testing with Chrome

  1. GoogleWikipediaTest Projectにdriverフォルダを作成します / You create a driver folder in GoogleWikipediaTest Project
    • GoogleWikipediaTest/driver
  2. chromedriver_win32.zipを取得します / You get chromedriver_win32.zip
  3. chromedriver_win32.zipを解凍して、chromedriver.exeをdriverフォルダに置きます
    • GoogleWikipediaTest/driver/chromedriver.exe
  4. GebConfig.groovyをGoogleWikipediaTest/src/main/resourcesに作成します / You create a GebConfig.groovy to GoogleWikipediaTest/src/main/resources
    • GoogleWikipediaTest/src/main/resources/GebConfig.groovy
  5. GebConfig.groovyにChromeの設定を追記します / You append the Chrome settings GebConfig.groovy
//choose "htmlunit", "firefox", "ie", "chrome"
driver = "chrome"

//chrome - http://chromedriver.storage.googleapis.com/index.html
System.setProperty("webdriver.chrome.driver", "driver/chromedriver.exe")

6. GoogleWikipediaMainTest.groovyを実行します / Run the GoogleWikipediaMainTest.groovy
7. Chromeが起動してテストが実行されればOKです / Chrome setting is the OK, if the test is up and running

4. IEによるWebテスト / Web testing with IE

  1. IEのセキュリティ設定を「保護モードを有効にする」で統一します / Unify the IE security settings in the "Enable Protected Mode"
    • インターネットオプション > セキュリティ
    • Internet Options > Security
    • f:id:yfj2:20141109003646p:plain
  2. Get IEDriverServer_Win32_2.47.0.zip
  3. 解凍してIEDriverServer.exeをdriverフォルダに置きます / Unzip to place the IEDriverServer.exe to driver folder
    • GoogleWikipediaTest/driver/IEDriverServer.exe
  4. GebConfig.groovyを修正します / Change the GebConfig.groovy
    • driver = "ie"
    • System.setProperty("webdriver.ie.driver", "driver/IEDriverServer.exe")
//choose "htmlunit", "firefox", "ie", "chrome"
driver = "ie"

//chrome - http://chromedriver.storage.googleapis.com/index.html
System.setProperty("webdriver.chrome.driver", "driver/chromedriver.exe")

//ie - http://selenium-release.storage.googleapis.com/index.html
System.setProperty("webdriver.ie.driver", "driver/IEDriverServer.exe")

6. GoogleWikipediaMainTest.groovyを実行します / Run the GoogleWikipediaMainTest.groovy
7. IEが起動してテストが実行されればOKです / IE setting is OK, if the test is up and running

5. FireFoxによるWebテスト / Web testing with FireFox

  1. GebConfig.groovyを修正します/ Change the GebConfig.groovy
    • driver = "firefox"
//choose "htmlunit", "firefox", "ie", "chrome"
driver = "firefox"

//chrome - http://chromedriver.storage.googleapis.com/index.html
System.setProperty("webdriver.chrome.driver", "driver/chromedriver.exe")

//ie - http://selenium-release.storage.googleapis.com/index.html
System.setProperty("webdriver.ie.driver", "driver/IEDriverServer.exe")

2. GoogleWikipediaMainTest.groovyを実行します / Run the GoogleWikipediaMainTest.groovy
3. firefoxが起動してテストが実行されればOKです / firefox setting is OK, if the test is up and running

6. まとめ / Summary

  1. driverフォルダにIEとChromeのライブラリを追加する / Add the IE and Chrome's library to driver folder
    • f:id:yfj2:20141109004706p:plain
  2. GebConfig.groovyに設定を追記する / Append the setting to GebConfig.groovy
    • driver
    • System.setProperty("webdriver.chrome.driver", "driver/chromedriver.exe")
    • System.setProperty("webdriver.ie.driver", "driver/IEDriverServer.exe")
  3. "driver=***"に「"chrome", "ie", "firefox"」のいずれかを設定することでクロスブラウザテストができる / Cross-browser tests can by setting to "driver = ***", " chrome " or " ie " or " firefox "
  • (ex)driver = "chrome"