読者です 読者をやめる 読者になる 読者になる

liguofeng29’s blog

個人勉強用ブログだっす。

groovy,geb(selenium),spockによる自動化テスト その10- GebConfig.groovy

GebConfig.groovyはGebの環境設定ファイルである。

参考: http://www.gebish.org/manual/1.1/

Geb attempts to load a ConfigSlurper script named GebConfig.groovy from the default package (in other words, in the root of a directory that is on the classpath). If it is not found, Geb will try to load a ConfigSlurper class named GebConfig from the default package - this is useful if you run tests that use Geb from an IDE because you won’t have to specify GebConfig.groovy as a resource, Geb will simply fall back to the compiled version of the script. If both script and class are not found Geb will continue using all defaults.

GebConfig.grooby

import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.firefox.FirefoxDriver

import geb.Browser

/*********/
/** URL **/
/*********/
// setBaseUrl(def baseUrl)
baseUrl = "http://localhost:8080"

/************/
/** Driver **/
/************/
// setDriver(WebDriver driver)
driver = {
    def driver = new FirefoxDriver()
    driver.javascriptEnabled = true
    driver
}

// 環境変数
environments {
    chrome {
        driver = { new ChromeDriver() }
    }
    firefox {
        driver = { new FirefoxDriver()}
    }
}

// sample : テストブラウザ切り替え
// mvn -Dgeb.env=chrome test
// or
// mvn -Dgeb.env=firefox test

/*************/
/** Waiting **/
/*************/
// setDefaultWaitTimeout(Double defaultWaitTimeout)
// setDefaultWaitRetryInterval(Double defaultWaitRetryInterval)

atCheckWaiting = true; // wait値がデフォルトtrueになる

waiting {
    timeout = 5
    retryInterval = 0.1
}

// setWaitPreset(String name, Double presetTimeout, Double presetRetryInterval)
waiting {
    presets {
        slow {
            timeout = 20
            retryInterval = 2
        }
        quick {
            timeout = 3
            retryInterval = 0.5
        }
    }
}

// Sample for waiting
Browser.drive {
    waitFor("quick"){ 2 == 1 + 1 }
}

/**************/
/** ???? **/
/**************/

// setUnexpectedPages(Collection pages)
unexpectedPages = [PageNotFoundPage, InternalServerErrorPage]


/**************/
/** Report **/
/**************/
/**  **/
// setReportsDir(File reportsDir)
reportsDir = new File("target/geb-reports")// 報告格納先

reportOnTestFailureOnly = true            // 失敗時のみ報告

// PageSourceReporter                      // only HTML
// ScreenshotAndPageSourceReporter         // HTML And png
// reporter = new CustomReporter()         // カスタム報告出力オブジェクト

// API
// void report(String label)
// void reportGroup(String path)
// cleanReportGroupDir()

groovy,geb(selenium),spockによる自動化テスト その9 - Page Object Pattern - Module

複数の画面で使う共通部品(メニュとか)はMoudleを継承することで再利用できる。
import geb.Browser
import geb.Module
import geb.Page

Browser.drive {
    to YahooTopUseModule

    検索モジュール.検索欄.value("検索内容")
    検索モジュール.検索ボタン.click()

    sleep 10 * 1000 // for debug
}.quit()

class YahooSearchModule extends Module {
    static content = {
        検索欄 (required: true) { $('#srchtxt') }
        検索ボタン (required: true) { $('#srchbtn') }
    }
}

class YahooTopUseModule extends Page {

    static url = 'http://www.yahoo.co.jp/'

    static at = { title.endsWith("Yahoo! JAPAN") }

    static content = {
        // 検索欄 (required: true) { $('#srchtxt') }
        // 検索ボタン (required: true) { $('#srchbtn') }

        検索モジュール {module YahooSearchModule}
    }
}

groovy,geb(selenium),spockによる自動化テスト その8 - Page Object Pattern - Pageの属性

Pageオブジェクトの属性

url属性 : PageのURLを表す

・シナリオ

  1. 相対パス指定のpageに移動 (to ExamplePage1)
  2. 絶対パス指定のpageに移動 (to ExamplePage2)
  3. 引数指定のpageに移動 (to ExamplePageWithArguments)
  4. インスタンス引数指定のpageに移動 (to ExamplePageWithInstance, new Person(id : “12345”))
/**
 * Page Object
 *
 * URL
 * urlは baseUrl + パスで構成
 * Page Objectのurlは、相対、絶対両方OK
  */
import geb.Browser
import geb.Page

Browser.drive {
    // baseUrl
    config.baseUrl = "http://www.gebish.org/"

    to ExamplePage1

    sleep 3 * 1000 // for debug

    to ExamplePage2

    sleep 3 * 1000 // for debug

    to ExamplePageWithArguments, "args1", "args2"
    // will go to「http://www.gebish.org/manual/args1/args2」

    sleep 3 * 1000 // for debug

    to ExamplePageWithInstance, new Person(id : "12345")
    // will go to [http://www.gebish.org/manual/12345]
    sleep 3 * 1000 // for debug
}.quit()

/** 相対url **/
class ExamplePage1 extends Page {
    static url = "manual/current/all.html"
}

/** 絶対url **/
class ExamplePage2 extends Page {
    static url = "http://www.gebish.org/manual/current/#at-checking"
}

/** 引数ありurl **/
class ExamplePageWithArguments extends Page {
    static url = "http://www.gebish.org/manual"
}

/** インスタンスを引数とするurl **/
class ExamplePageWithInstance extends Page{
    static url = "http://www.gebish.org/manual/"

    String convertToPath(Person person) {
        person.id.toString()
    }
}

class Person {
    String id
}

f:id:liguofeng29:20170302221619g:plain

at属性 : 呼ばれたpageが予期したpageであるのかを検証する

・シナリオ

  1. Yahooに移動する (to YahooTopWithRightAt)
  2. 検索ボタンをクリックする
  3. Yahooページに移動する (YahooTopWithWrongAt )
  4. at処理でgeb.waiting.WaitTimeoutExceptionが発生する
/**
 * Page Object
 *
 * at
 * 呼ばれたpageが予期したpageであるのかを検証する
 */
import geb.Browser
import geb.Page

def keywords = 'javait.hatenablog.com'

Browser.drive {

    to YahooTopWithRightAt
    println 'ログイン画面 : ' + title
    検索(keywords)
    println 'ホーム画面 : ' + title


    // geb.waiting.WaitTimeoutException発生
    to YahooTopWithWrongAt
    println 'to YahooTopWithWrongAtのat条件を満たさないので、処理はここまでがこないはず'

    sleep 10 * 1000 // for debug
}.quit()


class YahooTopWithRightAt extends Page {

    static url = 'http://www.yahoo.co.jp/'

    static at = {
        title.endsWith("Yahoo! JAPAN")
    }

    static content = {
        検索欄 { $('#srchtxt') }
        検索ボタン { $('#srchbtn') }
    }

    // pageが提供するサービス
    void 検索 (String keywords) {
        検索欄 = keywords
        検索ボタン.click()
    }
}

class YahooTopWithWrongAt extends Page {

    static url = 'http://www.yahoo.co.jp/'

    static at = {
        // GebConfig.groovyに設定したtimeoutでエラー発生
        title.endsWith("xxxxxxxxxxxxxxx wrong")
    }

    static content = {
        検索欄 { $('#srchtxt') }
        検索ボタン { $('#srchbtn') }
    }

    // pageが提供するサービス
    void 検索 (String keywords) {
        検索欄 = keywords
        検索ボタン.click()
    }
}

Content DSL : pageの要素を定義する

/**
 * Page Object
 *
 * Content DSL
 * 引数:
 * required : default true
 *            要素が存在しない場合、RequiredPageContentNotPresent例外を投げるかどうか
 *
 * cache :  defalut false
 *          ページをリロードしないと値はそのままだ。
 *
 * to    :  指定pageのURLを開く
 * wait  :  defalut false 値、true, a string, a number, a 2 element list of numbers
 * toWait : default false 、toと組み合わせで使う
 *          主に非同期遷移とかの場合、ページ遷移を待つのか?未検証。
 *
 * page  :  defalut null
 */
import geb.Browser
import geb.Page

def testPage = new File('src/main/java/html/script24.html')

Browser.drive {
    to YahooTopTestRequired

    /** required 検証 **/
    /** http://www.gebish.org/manual/current/#code-required-code **/
    try {
        println 存在しない要素1
    } catch (MissingPropertyException e) {
        println "required: true の場合は、MissingPropertyException発生 : " + 存在しない要素2
    }

    println "required: false の場合は、null : " + 存在しない要素2

    /** cache 検証 **/
    /** http://www.gebish.org/manual/current/#code-cache-code **/

    to YahooTopTestCache

    assert キャッシュ利用する == "value1"
    assert キャッシュ利用しない == "value1"

    value1 = "value99"

    assert キャッシュ利用する == "value1"        // 値が変わらない
    // assert キャッシュ利用しない == "value99"   // 変わるはずだが・・・・ 公式ページのコードでもやったが。

    /** page 検証 **/
    // 通常IFrame操作で利用する。

    /** to 検証 **/
    /* The to option allows the definition of which page the browser will be sent to if the content is clicked. */
    /** http://www.gebish.org/manual/current/#content-dsl-to **/
    config.baseUrl = new File(testPage.getAbsolutePath()).toURI()
    to PageTestTo
    リンク.click()

    /** NOTE
   *
   * toを使う際に、指定pageにはatを実装すべきである
   * toを指定することで実際に遷移が行われるわけではなく、指定pageのatが実行される。
   *
   * テキスト.click()をクリックしてもページ遷移は発生しない。
   */

     sleep 10 * 1000 // for debug
}.quit()

class YahooTopTestRequired extends Page {

    static url = 'http://www.yahoo.co.jp/'

    static at = { title.endsWith("Yahoo! JAPAN") }

    static content = {
        検索欄 (required: true) { $('#srchtxt') }
        検索ボタン (required: true) { $('#srchbtn') }

        存在しない要素1 (required: true) { $('#abcde1').text() }
        存在しない要素2 (required: false) { $('#abcde2').text() }
    }
}

class YahooTopTestCache extends Page {

    def value1 = "value1"

    static url = 'http://www.yahoo.co.jp/'

    static at = { title.endsWith("Yahoo! JAPAN") }

    static content = {
        検索欄 (required: true) { $('#srchtxt') }
        検索ボタン (required: true) { $('#srchbtn') }

        キャッシュ利用する   (cache: true) { value1 }
        // キャッシュ利用しない (cache: false) { value1 }
        キャッシュ利用しない  { value1 }
    }
}

class PageTestTo extends Page {
    static url = "script24.html"
    static content = {
        テキスト(to : GoogleTopTestTo) {$('#div1')}
        リンク (to : GoogleTopTestTo) {$('#googleLink')}
    }
}

class GoogleTopTestTo extends Page {
    static url = 'http://www.google.co.jp/'
    static at = { waitFor { title.startsWith("Google")} }
}

groovy,geb(selenium),spockによる自動化テスト その7 - Page Object Pattern

gebのPage Object

Page Object Patternとは?

PageObjectデザインパターンとは、アプリケーションの画面を1つのオブジェクトとしてとらえるデザインパターンの1種のことです。 Seleniumの公式サイトでも推奨されている、保守性の高いテストコードの書き方です。

公式サイトに記載されているPageObjectの原則は以下のようなものです。
・The public methods represent the services that the page offers(publicメソッドは、ページが提供するサービスを表す)
・Try not to expose the the internals of the page (ページの内部を公開しないこと)
・Generally don’t make assertions (原則としてassertionを行わないこと)
・Methods return other PageObjects (メソッドは他のPageObjectsを返す)
・Need not represent an entire page (ページ全体を表す必要はない)
・Different results for the same action are modelled as different as different methods (同じアクションに対して異なる結果となる場合には異なるメソッドとしてモデル化する)

参考:http://softwaretest.jp/labo/tech/labo-292/

geb.Page

シナリオ :
  1. yahoo.co.jpを開く
  2. 検索文字を入力
  3. 検索ボタンをクリック
import geb.Browser
import geb.Page

def keywords = 'javait.hatenablog.com'

Browser.drive {

    to YahooTop
    println 'ログイン画面 : ' + title

    検索(keywords)

    println 'ホーム画面 : ' + title

    waitFor {
        $('h3').size() > 0
    }

    $('h3').each { println "* ${it.text()}" }

    sleep 10 * 1000 // for debug

}.quit()

/**
 * Page Object
 *
 * url:相対パス、絶対パス
 * at: page確認条件
 * content DSL:pageの要素
 */
class YahooTop extends Page {

    static url = 'http://www.yahoo.co.jp/'

    static at = {
        // String geb.Page.getTitle()
        waitFor { title.endsWith("Yahoo! JAPAN")
    } }

    static content = {
        検索欄 { $('#srchtxt') }
        検索ボタン { $('#srchbtn') }
    }

    // pageが提供するサービス
    void 検索 (String keywords) {
        検索欄 = keywords
        検索ボタン.click()
    }
}

f:id:liguofeng29:20170302220102g:plain

groovy,geb(selenium),spockによる自動化テスト その6

gebでjavascriptを利用する

  1. javascript変数を取得
  2. メソッド呼び出し
  3. js追加

script6.html

<html>
<title>for javascript</title>
<script type="text/javascript">
   var var1 = 100;

   function add(a, b) {
       return a + b;
   }
</script>
<body>
</body>
</html>

script6_js.groovy

/**
 * Geb for javascript
 *
 * http://www.gebish.org/manual/
 *
 * 1. get variable from javascript
 * 2. call method
 * 3. add script
 */
import geb.Browser

def testPage = new File('src/main/java/html/script6.html')

Browser.drive {
    // 指定URLでブラウザオープン
    go testPage.toURI().toString()

    // JavascriptInterface geb.Browser.getJs()

    // get variable
    assert js.var1 == 100
    // 2. call method
    assert js.add(1, 10) == 11
    // 3. add script
    assert js."document.title" == "for javascript"
    js."alert('add script')"

    sleep 10 * 1000
}.quit()

f:id:liguofeng29:20170301210637g:plain

gebでpopupを制御する

SeleniumのWebDriverはalert, confirm, promptに対するソリューションを提供してないらしい。
gebでは、AlertAndConfirmSupporでalert,confirmに対する操作が可能。
※ promptはサポートしてないらしい。

API from AlertAndConfirmSuppor

  • alert

    • def withAlert(Closure actions)
    • def withAlert(Map params, Closure actions)
    • void withNoAlert(Closure actions) → 意図してない個所でalertが発生してするとAssertionError発生
  • confirm

    • def withConfirm(boolean ok, Closure actions)
    • def withConfirm(Closure actions)
    • def withConfirm(Map params, Closure actions)
    • def withConfirm(Map params, boolean ok, Closure actions)
    • void withNoConfirm(Closure actions)
/**
 * Geb for popup
 *
 * http://www.gebish.org/manual/
 *
 * use AlertAndConfirmSupport for alert,confirm,prompt
 *
 * Geb does not provide any support for prompt() due to its infrequent and generally discouraged use.

 */
import geb.Browser
import geb.Page

def testPage = new File('src/main/java/html/script7.html')

Browser.drive {
    // baseUrl設定
    config.baseUrl = new File(testPage.getAbsolutePath()).toURI()

    to PopupPage

    assert withAlert(wait: true) { showAlert.click() } == "alert!"
    //assert withAlert(wait: true) { showAlert.click() } == "some alert!" // エラー

    // actionsでalertが表示されるとAssertionError発生する
    withNoAlert { $("input", name: "dontShowAlert").click() }

    assert withConfirm(true) { showConfirm.click() } == "confirm?"
    sleep 2 * 1000
    assert withConfirm(false) { showConfirm.click() } == "confirm?"

    sleep 10 * 1000
}.quit()

class PopupPage extends Page{
    // baseURL +
    static url = "script7.html"
    static content = {
        showAlert      {$("input", name: "showAlert")}
        donotShowAlert {$("input", name: "dontShowAlert")}
        showConfirm    {$("input", name: "showConfirm")}
    }
}

f:id:liguofeng29:20170301211736g:plain

waitFor

Ajaxなので非同期処理の際に、処理完了を待つ際に使う。
geb.PageクラスのAPIである。

/**
 * Geb for waitFor
 * http://www.gebish.org/manual/
 * Ajaxなど処理待ちが発生する場合、waitForを利用して待つことができる
 *
 * API:
 *
 * waitFor {}           デフォルト
 * waitFor(10) {}       最長10秒待ち
 * waitFor(10, 0.5){}   最長10秒待ち、 0.5秒間隔
 * waitFor("quick"){}   GebConfig.groovyの設定
 *
 *
 * Geb does not provide any support for prompt() due to its infrequent and generally discouraged use.

 */
import geb.Browser

Browser.drive {
    go 'https://www.google.com/recaptcha/demo/ajax'

    $('input', value: 'Click Me').click()
    waitFor {
        $('#recaptcha_challenge_image').size() > 0
    }
    println $('#recaptcha_challenge_image').attr('src')

    sleep 10 * 1000
}.quit()

f:id:liguofeng29:20170301212218g:plain

WebAPI呼び出し

{
    "name": "Alex",
    "age": "20"
}
/**
 * Geb for calling api
 * http://www.gebish.org/manual/
 */
import geb.Browser
import groovy.json.JsonSlurper

def testPage = new File('src/main/java/json/json-data1.json')

Browser.drive {
    URL apiUrl = testPage.toURL()
    def data = new JsonSlurper().parseText(apiUrl.text)

    println data.name
    println data.age

    assert data.name == 'Alex'
    assert data.age == '20'

}.quit()