鍍金池/ 教程/ Java/ 測試指南
Grape 依賴管理器
與 Java 的區(qū)別
語法風格指南
Groovy 開發(fā)工具包
領域專用語言
安全更新
Groovy 與應用的集成
運行時及編譯時元編程(end)
測試指南
安裝 Groovy
設計模式
Groovy 的下載

測試指南

1 簡介

Groovy 對測試編寫帶有原生支持。除了語言特性以及與最先進的測試庫及架構相集成,Groovy 生態(tài)系統內還誕生了大量的測試庫及架構。

本章將先介紹語言的專有測試功能,接著將詳細介紹 JUnit 集成,用于規(guī)范的 Spock,以及用于功能測試的 Geb。最后,再概述適用于 Groovy 的其他測試庫。

2 語言特性

除了集成了對 JUnit 的支持外,Groovy 的一些原生功能已被證明非常適合測試驅動的開發(fā)。本節(jié)將進行相關介紹。

2.1 強力斷言語句

編寫測試意味著要指定假設,這就要使用斷言。在 Java 中,這可以通過 assert 關鍵字(在 J2SE 1.4 中引入)來實現,assert 語句可以通過 JVM 參數 -ea(或 -enableassertions)和 -da(或 -disableassertions)來啟用。斷言語句在 Java 中默認是禁用的。

Groovy 的斷言是 assert 的一種功能強大的變體,也被稱為強力斷言語句power assertion statement)。Groovy 的強力斷言語句與 Java 的 assert 的區(qū)別在于輸出中的布爾表達式會驗證為 false。

def x = 1
assert x == 2

// Output:   1??          
//
// Assertion failed:
// assert x == 2
//        | |
//        1 false  

1?? 表示標準錯誤輸出

如果斷言無法成功驗證,則拋出 java.lang.AssertionError,包含一個原始異常消息的擴展版本。強力斷言輸出顯示了從外在表達式到內在表達式的解析結果。

強力斷言語句真正的能力可以體現在復雜的布爾語句中,以及與集合或其他可使用 toString 的類相關的語句中。

def x = [1,2,3,4,5]
assert (x << 6) == [6,7,8,9,10]

// Output:
//
// Assertion failed:
// assert (x << 6) == [6,7,8,9,10]
//         | |     |
//         | |     false
//         | [1, 2, 3, 4, 5, 6]
//         [1, 2, 3, 4, 5, 6]

另一個與 Java 所不同的是,Groovy 斷言是默認啟用的。出于語言設計的決策,去除了使斷言無效的功能。或者,如同 Bertrand Meyer 所言:如果真的下水,最好帶著游泳圈。

另外一個值得注意的是,布爾表達式中帶有一定副作用的方法。內部錯誤消息構建機制只能存儲目標的實例引用,所以在遇到涉及副作用方法的情況時,錯誤消息文本在呈現時間上會出現異常。

assert [[1,2,3,3,3,3,4]].first().unique() == [1,2,3]

// Output:
//
// Assertion failed:
// assert [[1,2,3,3,3,3,4]].first().unique() == [1,2,3]
//                          |       |        |
//                          |       |        false
//                          |       [1, 2, 3, 4]
//                          [1, 2, 3, 4]           1??

1?? 錯誤消息顯示的是集合的實際狀態(tài),而不是應用了 unique 方法之前的狀態(tài)。

如果提供自定義斷言錯誤消息,可以使用 Java 的 assert expression1 : expression2,其中 expression1 是布爾表達式,而 expression2 是自定義錯誤消息。注意,這樣做會禁用強力斷言,完全回退到自定義錯誤消息上。

2.2 模擬與存根

對一些模擬與存根方案,Groovy 提供了強大的內建支持。使用 Java 時,動態(tài)模擬框架是常用的,其關鍵原因就在于利用 Java 手動創(chuàng)建模擬是一種很繁重的工作。這樣的框架可以輕松地應用于 Groovy 中,但創(chuàng)建自定義模擬顯然更為輕松。利用簡單的映射或閉包來創(chuàng)建自定義模擬。

下面就來介紹如何只利用 Groovy 的語言特性來創(chuàng)建模擬與存根。

2.2.1 映射強制

使用 map 或 expando,可以輕松包含協作對象(collaborator)的預期行為:

class TranslationService {
    String convert(String key) {
        return "test"
    }
}

def service = [convert: { String key -> 'some text' }] as TranslationService
assert 'some text' == service.convert('key.text')

as 操作符強制將映射轉換為特定類。給出的映射鍵被解析為方法名,而映射值,groovy.lang.Closure 塊,則被解析為方法代碼塊。

注意,如果用 as 操作符來處理 java.util.Map 的后代類,映射強制會產生妨礙。映射強制機制只針對特定的集合類,并沒有考慮到自定義類。

2.2.2. 閉包強制

as 操作符能以一種簡潔的形式用于閉包,從而非常適合開發(fā)者在簡單環(huán)境下進行測試。雖然該技術還沒有強大到能不再使用動態(tài)模擬的程度,但至少足以應付簡單環(huán)境。

持有單一方法的類或接口,包括 SAM(單一抽象方法)的類,可以用于強制閉包,使其成為一種指定類型的對象。為了實現這種機制,Groovy 內部會創(chuàng)建一個指定類的代理子對象。因此對象不是指定類的直接實例。這一點是非常重要的,比如生成的代理對象元類后續(xù)被改動。

強制閉包成為指定類型對象的范例如下:

def service = { String key -> 'some text' } as TranslationService
assert 'some text' == service.convert('key.text')

Groovy 支持一種叫做隱式 SAM 強制的功能。這意味著 as 操作符并不一定會用在運行時能夠推斷目標 SAM 類型的情況下。這種強制非常適用于模擬整個 SAM 類。

abstract class BaseService {
    abstract void doSomething()
}

BaseService service = { -> println 'doing something' }
service.doSomething()
2.2.3. MockFor 和 StubFor

Groovy 的模擬及存根類位于 groovy.mock.interceptor 包中。

MockFor 類支持獨立地對類進行測試(通常是單元測試),這是通過定義一種嚴格有序的協作對象行為來實現的。典型的測試情境通常會包含待測類及一個或多個協作對象。通常希望只對待測類的業(yè)務邏輯進行測試。為此,實現策略之一是通過簡化的模擬對象來代替協作對象,以便隔離出測試目標內的邏輯。MockFor 類可以利用元編程來創(chuàng)建這樣的模擬。協作對象的期望行為被定義為一種行為規(guī)范。行為會被自動強制執(zhí)行并檢查。

假設目標類如下所示:

class Person {
    String first, last
}

class Family {
    Person father, mother
    def nameOfMother() { "$mother.first $mother.last" }
}

利用 MockFor,模擬期望常常是序列相關的,自動會在結尾處調用 verify

def mock = new MockFor(Person)    1??    
mock.demand.getFirst{ 'dummy' }
mock.demand.getLast{ 'name' }
mock.use {                         2?? 
    def mary = new Person(first:'Mary', last:'Smith')
    def f = new Family(mother:mary)
    assert f.nameOfMother() == 'dummy name'
}
mock.expect.verify()              3??    

1?? 通過 MockFor 的一個新實例創(chuàng)建一個新模擬
2?? Closure 被傳入 use,啟用模擬功能
3?? 調用 verify 查看是否序列和方法調用數正如預期

StubFor 類支持獨立地對類進行測試(通常是單元測試),允許定義的協作對象的期望次序松散loosely-ordered)。通常測試情境包括一個受測類以及一個或多個協作對象。這樣的情境通常只希望測試 CUT 的業(yè)務邏輯。為此可以實施這樣一種策略:利用簡化的存根對象來代替協作實例,以便將目標類中的邏輯抽取出來。StubFor 允許使用元編程來創(chuàng)建這樣的存根。協作對象的預期行為被定義為一種行為規(guī)范。

MockFor 不同的是,利用 verify 檢查的存根期望是序列無關的,使用是可選的:

def stub = new StubFor(Person)       1??    
stub.demand.with {                    2??
    getLast{ 'name' }
    getFirst{ 'dummy' }
}
stub.use {                          3??
    def john = new Person(first:'John', last:'Smith')
    def f = new Family(father:john)
    assert f.father.first == 'dummy'
    assert f.father.last == 'name'
}
stub.expect.verify()                4??  

1?? 通過 StubFor 新實例創(chuàng)建的一個新存根。
2?? 使用 with 方法將所有閉包中的調用委托給 StubFor 實例。
3?? Closure 傳入 use,啟用存根功能。
4?? 調用 verify(可選的)檢查是否調用數目符合預期。

MockForStubFor 無法應用于靜態(tài)編譯類,比如使用 @CompileStatic 的 Java 類或 Groovy 類。要想存根或模擬這些類,可以使用 Spock 或一種 Java 模擬庫。

2.2.4 Expando 元類 (EMC)

Groovy 包含了一種特殊的 MetaClass:EMC(Expando 元類,ExpandoMetaClass)。允許使用簡潔的閉包格式來動態(tài)添加方法、構造函數、屬性、靜態(tài)方法。

每個 java.lang.Class 都帶有一個特殊的 metaclass 屬性,該屬性引用了一個 ExpandoMetaClass 實例。expando 元類并不局限于自定義類,它也可以用于 JDK 類,比如 java.lang.String

String.metaClass.swapCase = {->
    def sb = new StringBuffer()
    delegate.each {
        sb << (Character.isUpperCase(it as char) ? Character.toLowerCase(it as char) :
            Character.toUpperCase(it as char))
    }
    sb.toString()
}

def s = "heLLo, worLD!"
assert s.swapCase() == 'HEllO, WORld!'

ExpandoMetaClass 是相當好的一種用于模擬功能的備選方案,可以實現一些更先進的事務,比如模擬靜態(tài)方法。

class Book {
    String title
}

Book.metaClass.static.create << { String title -> new Book(title:title) }

def b = Book.create("The Stand")
assert b.title == 'The Stand'

或甚至構造函數:

Book.metaClass.constructor << { String title -> new Book(title:title) }

def b = new Book("The Stand")
assert b.title == 'The Stand'

模擬構造函數可能似乎是一種討巧的方法,最好甚至不用考慮,但有效的用例也還是存在的,在 Grails 上就能找到一些范例:在運行時中,借助 ExpandoMetaClass 添加域類構造函數。域對象在 Spring 應用上下文中自我注冊,并實現了由依賴項注入容器所控制的服務或 Bean 的注入。

如果希望改變每個測試方法級別上的 metaClass 屬性,需要清除作用于元類上的更改,否則這些更改將持續(xù)作用于測試方法調用的整個過程之中。GroovyMetaClassRegistry 中替代元類就能去掉更改。

GroovySystem.metaClassRegistry.setMetaClass(java.lang.String, null)

注冊 MetaClassRegistryChangeEventListener,跟蹤改變的類,并清除選定的測試運行時的 cleanup 方法中的更改??梢栽?Grails Web 開發(fā)框架中找到比較好的范例。

除了使用類級別的 ExpandoMetaClass,也支持使用元類或對象級別。

def b = new Book(title: "The Stand")
b.metaClass.getTitle {-> 'My Title' }

assert b.title == 'My Title'

在該例中,元類更改只與實例有關,根據測試情境,這可能要比全局元類更改適應性更好。

2.3 GDK 方法

下面將概述可以在測試用例情境(比如對于數據生成的測試)中使用的 GDK 方法。

2.3.1 Iterable##combinations

利用 java.lang.Iterable 兼容類中添加的 combinations 方法,可以從一個包含兩個或更多子列表的列表中獲得一個組合列表:

void testCombinations() {
    def combinations = [[2, 3],[4, 5, 6]].combinations()
    assert combinations == [[2, 4], [3, 4], [2, 5], [3, 5], [2, 6], [3, 6]]
}

該方法可以在測試用例情境下,針對特定的方法調用,生成所有可能的參數組合。

2.3.2 Iterable##eachCombination

利用添加到 java.lang.Iterable 兼容類中的 eachCombination 方法,如果組合是由 combinations 方法所構建的,那么它可以在每一個組合上應用一個函數(或者如同在該例中這樣采用 groovy.lang.Closure)。

eachCombination 是一種添加到所有符合 java.lang.Iterable 接口的類上的 GDK 方法。它會在輸入列表的每一個組合上應用一個函數:

void testEachCombination() {
    [[2, 3],[4, 5, 6]].eachCombination { println it[0] + it[1] }
}

該方法還可以用在測試上下文中,利用每一個生成的組合來調用方法。

2.4 工具支持

2.4.1 測試代碼覆蓋率

代碼覆蓋率是關于(單元)測試有效性的一種重要衡量標準。具有較高代碼覆蓋率的程序要比代碼覆蓋率較低的程序更安全,留存嚴重 bug 的幾率要低得多。要想提示代碼覆蓋率,生成的字節(jié)碼通常需要在執(zhí)行前進行檢測。Cobertura 就是受 Groovy 支持的用于此目的一款工具。

很多框架及構建工具都集成有 Cobertura。Grails 中有基于 Cobertura 的 code coverage plugin;Gradle 中則有 gradle-cobertura plugin。當然,它們僅僅是眾多插件中的兩個而已。

下例展示了如何在一個 Groovy 項目的 Gradle 構建腳本中啟用 Cobertura 代碼覆蓋報告:

def pluginVersion = '<plugin version>'
def groovyVersion = '<groovy version>'
def junitVersion = '<junit version>'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.eriwen:gradle-cobertura-plugin:${pluginVersion}'
    }
}

apply plugin: 'groovy'
apply plugin: 'cobertura'

repositories {
    mavenCentral()
}

dependencies {
    compile "org.codehaus.groovy:groovy-all:${groovyVersion}"
    testCompile "junit:junit:${junitVersion}"
}

cobertura {
    format = 'html'
    includes = ['**/*.java', '**/*.groovy']
    excludes = ['com/thirdparty/**/*.*']
}

Cobertura 代碼覆蓋報告和測試代碼覆蓋報告可以添加到持續(xù)集成構建任務中,可以為這些報告選擇一些輸出格式。

3 利用 JUnit 3 和 4 進行單元測試

Groovy 簡化了 JUnit 測試,使其更具有 Groovy 的特點。下面就來探討一下 JUnit 3/4 與 Groovy 的集成情況。

3.1. JUnit 3

或者在 Groovy 類中,最顯著支持 JUnit 3 測試的一個類是 GroovyTestCase 類。由于派生自 junit.framework.TestCase,所以它提供了大量的額外方法,使 Groovy 測試變得易如反掌。

雖然 GroovyTestCase 繼承自 TestCase,但這并不意味在項目中無法使用 JUnit 4 的一些特性。實際上,最近發(fā)布的一些 Groovy 版本都帶有打包的 JUnit 4,并且?guī)в袑?TestCase 的后向支持實現。在 Groovy 郵件列表中的一些對是否使用 GroovyTestCase 或 JUnit 4 的討論中,人們認為這種選擇更多是由個人口味來決定的,但利用 GroovyTestCase ,你能免費使用大量的方法,便于編寫特定類型的測試。

下面就來看看 GroovyTestCase 所提供的一些方法,完整的方法列表位于 [groovy.util.GroovyTestCase] (http://docs.groovy-lang.org/2.4.5/html/gapi/index.html?groovy/util/GroovyTestCase.html) 的 JavaDoc 文檔中。另外,不要忘記它繼承自 junit.framework.TestCase,后者繼承了所有的 assert* 方法。

3.1.1 斷言方法

GroovyTestCase 繼承自 junit.framework.TestCase,因此也間接繼承了大量的斷言方法,從而可以應用到每一個測試方法中:

class MyTestCase extends GroovyTestCase {

    void testAssertions() {
        assertTrue(1 == 1)
        assertEquals("test", "test")

        def x = "42"
        assertNotNull "x must not be null", x
        assertNull null

        assertSame x, x
    }

}

如上所示,與 Java 不同,可以在大多數場合忽略括號,從而可以使 JUnit 斷言方法調用表達式實現更好的可讀性。

assertScript 是一種由 GroovyTestCase 添加的有趣方法,它能保證指定的 Groovy 代碼字符串成功執(zhí)行,不會導致任何異常。

3.1.2 shouldFail 方法

shouldFail 用于查看指定代碼塊是否失敗,如果失敗,斷言成立,否則斷言失敗。

void testInvalidIndexAccess1() {
    def numbers = [1,2,3,4]
    shouldFail {
        numbers.get(4)
    }
}

上例使用了基本的 shouldFail 方法接口,使用了一個 groovy.lang.Closure 做單一參數。Closure 持有的代碼被認為會在運行時中止。

如果我們要對特定 java.lang.Exception 類型進行 shouldFail 斷言,我們可以使用一個 shouldFail 實現,其中第一個參數為 Exception 類,第二個參數為 Closure 類。

void testInvalidIndexAccess2() {
    def numbers = [1,2,3,4]
    shouldFail IndexOutOfBoundsException, {
        numbers.get(4)
    }
}

如果拋出 IndexOutOfBoundsException (或其后代類),則測試用例失敗。

shouldFail 其實還有一個隱藏的優(yōu)點:返回異常消息。如果想對異常錯誤消息進行斷言,這一點很有用。

void testInvalidIndexAccess3() {
    def numbers = [1,2,3,4]
    def msg = shouldFail IndexOutOfBoundsException, {
        numbers.get(4)
    }
    assert msg.contains('Index: 4, Size: 4')
}
3.1.3 notYetImplemented 方法

notYetImplemented 方法受 HtmlUnit 影響很大。允許編寫一個測試方法,但把它標記為還未實現。只要測試方法失敗,并且被標記為 notYetImplemented,測試就依然能夠通過。

void testNotYetImplemented1() {  
    if (notYetImplemented()) return     1??

    assert 1 == 2                        2??   
}

1?? 要想使 GroovyTestCase 獲取當前方法堆棧,就需要調用 notYetImplemented。
2?? 只要測試結果為 false,測試執(zhí)行就會成功。

可以用 @NotYetImplemented 注釋來替代 notYetImplemented 方法。它能注釋一個未實現方法,帶有和 GroovyTestCase##notYetImplemented 相同的行為,只是不需要調用 notYetImplemented 方法。

@NotYetImplemented
void testNotYetImplemented2() {
    assert 1 == 2
}

3.2 JUnit 4

利用 Groovy 編寫 JUnit 4 測試用例沒有任何限制。groovy.test.GroovyAssert 可以保存各種靜態(tài)方法,它們可以用于替代 JUnit 4 測試中的 GroovyTestCase 方法。

import org.junit.Test

import static groovy.test.GroovyAssert.shouldFail

class JUnit4ExampleTests {

    @Test
    void indexOutOfBoundsAccess() {
        def numbers = [1,2,3,4]
        shouldFail {
            numbers.get(4)
        }
    }

}

如上所示,GroovyAssert 中的靜態(tài)方法都在類定義之前導入進來,所以 shouldFail 可以像在 GroovyTestCase 中那樣使用。

由于 groovy.test.GroovyAssert 來源自 org.junit.Assert,所以它繼承了 JUnit 所有的斷言方法。但由于強力斷言語句的引入,依賴斷言語句成了一種良好實踐,不再需要 JUnit 斷言方法,而改善的消息也成為了這樣做的一個主要因素。

GroovyAssert.shouldFail 并不絕對等于 GroovyTestCase.shouldFail。GroovyTestCase.shouldFail 返回異常消息,GroovyAssert.shouldFail 返回異常本身,獲取異常消息還需要寫一些代碼,但反過來說也不是沒有好處,你可以訪問異常的其他屬性和方法:

@Test
void shouldFailReturn() {
    def e = shouldFail {
        throw new RuntimeException('foo',
                                   new RuntimeException('bar'))
    }
    assert e instanceof RuntimeException
    assert e.message == 'foo'
    assert e.cause.message == 'bar'
}

4 利用 Spock 測試

Spock 是用于 Java 與 Groovy 程序的一種測試和規(guī)范框架。優(yōu)雅以及高度表達規(guī)范的 DSL 都是它脫穎而出的重要因素。在實踐中,Spock 規(guī)范會以 Groovy 類的形式呈現。雖然編寫為 Groovy 類形式,但卻可以用于測試 Java 類。Spock 可用于進行單元測試、集成測試,以及 BDD 測試(行為驅動的開發(fā)),它并不局限于某一特定類別的測試框架或庫。

除了這些非常驚艷的功能外,對于如何在第三方庫中利用先進的 Groovy 編程語言特性(比如使用 Groovy AST 轉換)這種問題,Spock 也堪稱最佳答案。

這一部分內容并不詳細介紹 Spock 的具體使用細節(jié),只概述了 Spock 的一些基本內容以及它在各種測試中的應用情況(單元測試、集成測試、功能測試以及其他類型的測試)。

下面首先剖析 Spock 規(guī)范,它能讓你迅速了解 Spock 的功能。

4.1 規(guī)范

通過 Spock,我們可以編寫描述相關系統功能(屬性、方面)的規(guī)范。這里所說的“系統”可以是任何東西,既可以是單個類,也可以是整個的應用程序,更準確的描述應該是“遵循某種規(guī)范的系統”。“功能描述”始于系統與其協作對象的一個特殊快照,這種快照被稱為“功能夾具”。

Spock 規(guī)范類衍生自 spock.lang.Specification。具體的規(guī)范類可能含有字段、夾具方法、功能方法以及輔助方法。

下面來看看一個假想的 Stack 類的簡單規(guī)范,它只帶有一個功能方法:

class StackSpec extends Specification {

    def "adding an element leads to size increase"() {    1??
        setup: "a new stack instance is created"        2??
            def stack = new Stack()

        when:                                           3??
            stack.push 42

        then:                                           4??
            stack.size() == 1
    }
}

1?? 功能方法,按照歸約,以字符串字面量形式命名。
2?? 設置塊,包含用于功能所需完成工作的所有設置內容。
3?? When 語句塊描述了一種刺激性條件,由功能規(guī)范所確定的一種目標特定行為。 4?? Then 語句塊包含的表達式能夠驗證由 When 語句塊觸發(fā)的代碼結果。

Spock 功能規(guī)范被定義為 spock.lang.Specification 類中的方法。它們使用字符串字面量而不是方法名來描述功能。

功能方法持有多個語句塊,在我們這個小例子中,使用了 setupwhenthen。setup 很特殊,它是可選的,可以用來配置出現在功能方法中的本地變量。when 語句塊定義了刺激條件,是描述該刺激條件響應的 then 語句塊的對應物。

注意,StackSpec 中的 setup 方法額外還存在一個描述字符串,這種描述字符串是可選的,可以添加到任何語句塊標簽(setup、whenthen)的后面。

4.2 更多 Spock 的詳細信息

Spock 還提供更多的高級功能,比如像數據表及高級模擬功能,等等。可參看 Spock GitHub page獲取更多信息及下載信息。

5 利用 Geb 進行功能測試

Geb 是一種功能性的 Web 測試及抓取庫,可以完美地與 JUnit 和 Spock 集成。它基于 Selenium Web 驅動,像 Spock 一樣,它也能提供 Groovy DSL 來編寫 Web 應用的功能測試。

以下這些功能使其成為一種非常好的功能測試庫:

  • 通過 jQuery 語句(比如 $ 函數)進行 DOM 訪問;
  • 實現頁面模式;
  • 支持特定 Web 組件(比如菜單欄)的模塊化
  • 通過 JS 變量與 JavaScript 相集成。

這一部分內容并不詳細介紹 Geb 的具體使用細節(jié),只概述了 Geb 的一些基本內容以及它在功能測試中的應用情況。

接下來就通過范例來介紹針對帶有一個搜索字段的 Web 頁面,如何利用 Geb 編寫功能測試。

5.1 Geb 腳本

雖然 Geb 可以單獨用于 Groovy 腳本,但在很多場合中,它是和其他測試框架聯合使用的。Geb 自帶很多可以用于 JUnit 3/4、TestNG 以及 Spock 中的基本類。這些基本類都是 Geb 額外模塊的一部分,需要以依賴的形式添加進來。

比如說,在下例中,在 JUnit 4 測試中,必須使用 @Grab 依賴來運行帶有 Selenium Firefox 驅動的 Geb。需要 JUnit 3/4 支持的模塊是 geb-junit

@Grapes([
    @Grab("org.gebish:geb-core:0.9.2"),
    @Grab("org.gebish:geb-junit:0.9.2"),
    @Grab("org.seleniumhq.selenium:selenium-firefox-driver:2.26.0"),
    @Grab("org.seleniumhq.selenium:selenium-support:2.26.0")
])

Geb 的中心類是 geb.Browser 類,正如其名稱所暗示的那樣,用來瀏覽頁面及訪問 DOM 元素:

def browser = new Browser(driver: new FirefoxDriver(), baseUrl: 'http://myhost:8080/myapp')                                  1?? 
browser.drive {
    go "/login"                               2??

    $("##username").text = 'John'               3??
    $("##password").text = 'Doe'

    $("##loginButton").click()

    assert title == "My Application - Dashboard"

1?? 創(chuàng)建了一個新的 Browser 實例。在本例中,它使用了 Selenium FirefoxDriver,并設置了 baseUrl
2?? go 用于導航至一個 URL 或相對 URL。
3?? $ 和 CSS 選擇器一起用于訪問 DOM 字段的 usernamepassword。

Browser 類帶有的 drive 方法會將所有方法或屬性的調用都委托給當前的 browser 實例。Browser 配置不能在行內完成,它可以設置在外部的 GebConfig.groovy 配置文件中。在實際中,Browser 類大多由 Geb 測試基本類所隱藏。它們會將所有丟失的屬性及方法調用都委托給存在于后臺中的當前 browser 實例。

class SearchTests extends geb.junit4.GebTest {

    @Test
    void executeSeach() {
        go 'http://somehost/mayapp/search'        1??          
        $('##searchField').text = 'John Doe'         2??    
        $('##searchButton').click()                    3??  

        assert $('.searchResult a').first().text() == 'Mr. John Doe'          4??
    }
}

1?? Browser##go 獲取相對或絕對鏈接,調用頁面。
2?? Browser##$ 用于訪問 DOM 內容??梢允褂糜蓾撛诘?Selenium 驅動所支持的任何 CSS 選擇器。
3?? click 用于點擊按鈕。
4?? $ 用于獲取 searchResult 塊的首個鏈接。

上例展示了一個利用 JUnit 4 基本類 geb.junit4.GebTest 的簡單 Geb Web 測試。注意在該例中,Browser 配置是放在外部的。GebTest 會將一些方法(go$)都委托給潛在的 browser 實例。

5.2 更多 Geb 相關內容

在之前的內容中,只介紹了一些 Geb 功能的皮毛,更多詳情參見其項目主頁。

上一篇:安全更新下一篇:Grape 依賴管理器