liguofeng29’s blog

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

groovy,geb(selenium),spockによる自動化テスト その11- Report機能

gebは検証pageに対するレポート機能(HTML,PNG)を提供している。

シナリオ

  1. リスナー定義&追加(レポートする度にprintlnが実行される)
  2. googleに移動する
  3. レポートする
  4. yahooに移動する
  5. レポートする
  6. googleに移動する
  7. レポートする
  8. yahooに移動する
  9. レポートする
import geb.Browser
import geb.report.ReportState
import geb.report.Reporter
import geb.report.ReportingListener

Browser.drive {

    // リスナ定義
    reportingListener = new ReportingListener() {
        void onReport(Reporter reporter, ReportState reportState, List<File> reportFiles) {
            reportFiles.each {
                println "[[ATTACHMENT|$it.absolutePath]]"
            }
        }
    }

    // リスナ追加
    config.reporter.addListener(reportingListener);

    go 'http://www.google.co.jp/'
    report 'google-'

    go 'http://www.yahoo.co.jp/'
    report 'yahoo-'


    /** reportGroup  **/
    reportGroup "google"
    go "http://google.com"
    report "home page"

    reportGroup "github"
    go "http://github.com"
    report "home page"


    // cleanReportGroupDir()

    sleep 30 * 1000
}
出力されるレポートファイル

f:id:liguofeng29:20170302224004p:plain

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