際際滷

際際滷Share a Scribd company logo
BDD and browser automation with
Geb and Spock
舒仆亳仍ミ 仂亞亟舒仆
Jeeconf | may 2014 | @bogdand
仍舒于仆亶 于仂仗仂 亢亳亰仆亳
于亠仍亠仆仆仂亶 亳 于亠亞仂 舒从仂亞仂
仍舒于仆亶 于仂仗仂 亢亳亰仆亳
于亠仍亠仆仆仂亶 亳 于亠亞仂 舒从仂亞仂
舒从 于 仍ミ頴笑 仄仂仄亠仆 弍 于亠亠仆仆仄 于
从舒亠于亠 亠从亠亶 于亠亳亳 于舒亠亞仂
仗亳仍仂亢亠仆亳 亳 亟舒 于仂亰仄仂亢仆仂 仍ミ観礆
仗仂仆 仂 亢亠 仂仆仂 亟亠仍舒亠?
仂 ?
 C Groovy / Grails 仆舒亳仆舒  2008 亞仂亟舒
 丕仗亠仆仂 于仆亠亟亳仍亳 亳 亳仗仂仍亰亠仄 Geb /
Spock
 - Groovy / Grails 从
 亠于亶 舒亰舒弍仂亳从 于 TransferWise,
亠亶舒 100+ 亠仍仂于亠从
TransferWise
TransferWise
弌 亠亞仂 于亠 仆舒亳仆舒仍仂?
 0% 仗仂从亳亠 亠舒仄亳
弌 亠亞仂 于亠 仆舒亳仆舒仍仂?
Smoke testing
 丐亠亳仂于舒仍仂 <10 舒仆亳
 亠仄 仗仂仂亢亟亠仆亳 亠仂于 1 仄亳仆舒
 弍仆舒亢亠仆仆 仂亳弍仂从 - 仄舒仍仂 =)
Smoke testing
Geb
very groovy browser automation
Geb
 Groovy 仂弍亠从舒 于仂从亞 Selenium 2
 亳亠仆亰亳 ASL2
 丐亠从舒 于亠亳: 0.9.2
 亢亳  JUnit 亳 Spock
 Groovy Page object
 http://gebish.org
Geb - WebDriver
 仂弍亳仍仆亠 弍舒亰亠:
 iPad
 iPhone
 Android
 Blackberry
 PhantomJS
 HtmlUnit
仂仂亶 Geb test
go "http://www.google.com"
$("input", name: "q").value("JeeConf")
$("button", name: "btnG").click()
waitFor { $("div", id: "search").displayed }
assert $("div", id: "search").text()
.contains("jeeconf.com")
仂仂亶 Geb test
go "http://www.google.com"
$("input", name: "q").value("JeeConf")
$("button", name: "btnG").click()
waitFor { $("div", id: "search").displayed }
assert $("div", id: "search").text()
.contains("jeeconf.com")
仂仂亶 Geb test
go "http://www.google.com"
$("input", name: "q").value("JeeConf")
$("button", name: "btnG").click()
waitFor { $("div", id: "search").displayed }
assert $("div", id: "search").text()
.contains("jeeconf.com")
仂仂亶 Geb test
go "http://www.google.com"
$("input", name: "q").value("JeeConf")
$("button", name: "btnG").click()
waitFor { $("div", id: "search").displayed }
assert $("div", id: "search").text()
.contains("jeeconf.com")
仂仂亶 Geb test
go "http://www.google.com"
$("input", name: "q").value("JeeConf")
$("button", name: "btnG").click()
waitFor { $("div", id: "search").displayed }
assert $("div", id: "search").text()
.contains("jeeconf.com")
仂仂亶 Geb test
go "http://www.google.com"
$("input", name: "q").value("JeeConf")
$("button", name: "btnG").click()
waitFor { $("div", id: "search").displayed }
assert $("div", id: "search").text()
.contains("jeeconf.com")
亠仂亟 $()
$(束css selector損, 束index or range損, 束attribute / text matchers損)
亳仄亠
$("div") // 于亠 div 仍亠仄亠仆
$("div", 0) // 仗亠于亶 div 仍亠仄亠仆
$("div", 0..2) // 仗亠于 亳 div 仍亠仄亠仆舒
// 丐亠亳亶 H2 仍亠仄亠仆  亠从仂仄 Geb
$("h2", 2, text: "Geb")
CSS3 亠仍亠从仂
$("div.some-class p:first[title='some']")
$("ul li a")
$("table tr:nth-child(2n+1) td")
$("div#content p:first-child::first-line")
亳弍
仂亳从 仗仂 舒亳弍舒仄
//<div foo="bar">
$("div", foo: "bar")
亳弍
仂亳从 仗仂 舒亳弍舒仄
//<div foo="bar">
$("div", foo: "bar")
仂亳从 仗仂 亠从
//<div>foo</div>
$("div", text: "foo")
舒于亳亞舒亳
仂亳从 仗仂 舒亳弍舒仄
//<div foo="bar">
$("div", foo: "bar")
仂亳从 仗仂 亠从
//<div>foo</div>
$("div", text: "foo")
仂亳从 仗仂 亠亞仍仆仄 于舒亢亠仆亳礆
//<div>foo</div>
$("div", text: ~/f.+/)
仗仂仄仂亞舒亠仍仆亠 仆从亳亳
$("p", text: startsWith("p"))
$("p", class: contains("section"))
$("p", id: endsWith(~/d/))
仂仍亠 仗亳仄亠仂于 于 亟仂从仄亠仆舒亳亳
仆仂亳亠仍仆亶 仗仂亳从
$("p").previous()
$("p").prevAll()
$("p").next()
$("p").parent()
$("p").siblings()
$("div").children()
$("p").nextAll(".listing")
Functional testing
Functional testing
 亠仄 仗仂仂亢亟亠仆亳 亠仂于 - 20 仄亳仆
 弍仆舒亢亠仆仆 仂亳弍仂从 - 亢亠 弍仂仍亠 =)
 磲 亠亟亳亰舒亶仆 于亠弍 舒仆亳
Geb
弌舒仆亳 亳 仄仂亟仍亳
Page Object
class WikipediaPage extends Page {
static url = "https://www.wikipedia.org"
static at = { title == "Wikipedia" }
static content = {
search { $("#searchInput") }
}
}
Page Object
class WikipediaPage extends Page {
static url = "https://www.wikipedia.org"
static at = { title == "Wikipedia" }
static content = {
search { $("#searchInput") }
}
}
Page Object
class WikipediaPage extends Page {
static url = "https://www.wikipedia.org"
static at = { title == "Wikipedia" }
static content = {
search { $("#searchInput") }
}
}
Page Object
class WikipediaPage extends Page {
static url = "https://www.wikipedia.org"
static at = { title == "Wikipedia" }
static content = {
search { $("#searchInput") }
}
}
Page Object 亳仗仂仍亰仂于舒仆亳亠
Browser.drive {
to WikipediaPage
assert at(WikipediaPage)
search = "JeeConf"
}
仗亳仂仆舒仍仆亶 从仂仆亠仆
class OptionalPage extends Page {
static content = {
errorMsg(required: false) { $("p.errorMsg") }
}
}
亳仆舒仄亳亠从亳亶 从仂仆亠仆
class DynamicPage extends Page {
static content = {
errorMsg(wait: true) { $("p.errorMsg") }
}
}
舒仍亠亟仂于舒仆亳亠 Page Object
 仍仂从亳 content 弍亟 仍亳
 亠仂亟 仆舒仍亠亟ム
仂于仂仆仂亠 亳仗仂仍亰仂于舒仆亳亠
 仍仂从亳, 仗仂于仂ム亳亠 仆舒 仄仆仂亞亳
舒仆亳舒
 仍仂从亳,
仗仂于仂ム亳亠
仆舒 仂亟仆仂亶
舒仆亳亠
弍磦仍亠仆亳亠 仄仂亟仍
class NavBarModule extends Module {
static content = {
homePageLink(to: HomePage) { $("a#home") }
profileLink(to: ProfilePage) { $("a#profile") }
}
}
仗仂仍亰仂于舒仆亳亠 仄仂亟仍亠亶
class HomePage extends Page {
static content = {
navBarModule { module NavBarModule }
}
void goToProfilePage() {
navBarModule.profileLink.click()
}
}
亳仄亠 moduleList
class ProfilePage extends Page {
static content = {
paymentListTable {moduleList PaymentRow,
$("table#paymentList tbody tr")}
}
}
class PaymentRow extends Module {
static content = {
amount { $("td.amount") }
status { $("td.status") }
}
}
仂弍仍亠仄舒 仗仂亟亟亠亢从亳
to HomePage
loginButton.click()
username = "user"
password = "password"
loginButton.click()
Page Object builder
 仂亟仂亟 仗亠亟仍仂亢亠仆 Craig Atkinson
 仂 仄仂仍舒仆亳 Geb 亟亠仍亠亞亳亠 于亰仂于
仄亠仂亟仂于 亠从亠亶 舒仆亳亠
 亳仍亟亠 亟亠仍舒亠 亳 于亰仂于 磦仆仄亳
Page Object builder
HomePage homePage = to HomePage
LoginPage loginPage = homePage.clickLoginButton()
DashboardPage dashboardPage = loginPage.login("user",
"password")
HomePage homePage = to HomePage
DashboardPage dashboardPage = homePage.clickLoginButton().
login("user", "password")
亰仂于 仗仂 亠仗仂从亠
亠舒仍亳亰舒亳 HomePage
class HomePage extends Page {
static content = {
loginButton(to: LoginPage, wait: true) {
$("#loginButton")
}
}
LoginPage clickLoginButton() {
loginButton.click()
return browser.page
}
}
Geb + Spock
亳亠从舒
App
Browser
WebDriverGeb
geb-spock
(Testing adapter)
Spok
Spock 亠
class GoogleSpec extends GebReportingSpec {
def "the first link should be wikipedia"() {
when:
to GoogleHomePage
q = "wikipedia"
then:
at GoogleResultsPage
firstResultLink.text() == "Wikipedia"
when:
firstResultLink.click()
then:
waitFor { at WikipediaPage }
}
}
Spock data-driven 亠
when:
LoginPage loginPage = loginAsUser(username)
then:
assert loginPage.error == expectedErrorMessage
where:
username | expectedErrorMessage
'disabledUser' | 'Sorry, your account is disabled'
'lockedUser' | 'Sorry, your account is locked'
'invalidUser' | 'Sorry, we could not find that account'
仂弍仍亠仄
 丐亠亳仂于舒仍亳 于 仂仆仂于仆仂仄 亟仍亳亠仍仆亠
亠仗仂从亳 舒仆亳
  仗仂亟亞仂仂于从舒 亟舒仆仆 亟亠仍舒仍舒 亠亠亰
于亠弍 亳仆亠亠亶
亠从舒亳于亶 亠
class LoginSpec extends GebReportingSpec {
def "login"() {
given: "a valid user"
go RegistrationPage
register("user", "password")
logout()
when: "the user logins with valid credentials"
go LoginPage
login("user", "password")
then: "the welcome page is displayed"
at DashboardPage
}
亠从舒亳于亶 亠
class LoginSpec extends GebReportingSpec {
def "login"() {
given: "a valid user"
go RegistrationPage
register("user", "password")
logout()
when: "the user logins with valid credentials"
go LoginPage
login("user", "password")
then: "the welcome page is displayed"
at DashboardPage
}
Use case 亠亳仂于舒仆亳亠
丼亠亞仂 仄 仂亳仄?
 亠亰仍舒 亠仂于 仆亠 弍仍亠于仂亠 亰仆舒亠仆亳亠
 亅从舒仆亳仂于舒仆亳亠 亠仆舒亳亠于
 丕仗仂 仆舒 亟仂从仄亠仆亳仂于舒仆亳亠
 亳于仍亠亠仆亳亠 仆亠亠仆亳亠从亳
仗亠亳舒仍亳仂于 亟仍 舒弍仂  亠舒仄亳
亅从舒仆亳仂于舒仆亳亠 亠仆舒亳亠于
丼亠亞仂 仄 仂亳仄 亟仂亳亞仆?
 舒亢亟亶 亠 亞仂仂于亳 亟舒仆仆亠 亟仍 亠弍
 丐亠 亟仂仍亢亠仆 亰仆舒 从舒从 仄仂亢仆仂 仄亠仆亠
亳仆仂仄舒亳亳 仂 于仆亠仆仆仂
仗亳仍仂亢亠仆亳
 丐亠 仆亠 亟仂仍亢亠仆 仍仂仄舒 仗亳
亠舒从仂亳仆亞舒
亠于舒 亳亟亠
 亟舒于舒亶亠 仂弍仆仂于仍 弍舒亰?
仂弍仍亠仄:
 丐亠 亰仆舒ム 仄仆仂亞仂 仆亳亰从仂仂于仆亠于仂亶
亳仆仂仄舒亳亳 仂 仗亳仍仂亢亠仆亳亳
 丐 仂亠仆 于亳亠仍仆 从
亳亰仄亠仆亠仆亳礆 于 仗亳仍仂亢亠仆亳亳
Remote control
Groovy remote control
is a library for executing closures defined in one
Groovy application to be executed in a different
(possible remote) Groovy application.
Remote control - 亠于亠
def receiver = new Receiver()
def handler = new RemoteControlHttpHandler(receiver)
def server =
HttpServer.create(new InetSocketAddress(8080), 0)
server.createContext("/groovy-rc", handler)
server.start()
Remote control - 亠于亠
def receiver = new Receiver()
def handler = new RemoteControlHttpHandler(receiver)
def server =
HttpServer.create(new InetSocketAddress(8080), 0)
server.createContext("/groovy-rc", handler)
server.start()
Remote control - 亠于亠
def receiver = new Receiver()
def handler = new RemoteControlHttpHandler(receiver)
def server =
HttpServer.create(new InetSocketAddress(8080), 0)
server.createContext("/groovy-rc", handler)
server.start()
Remote control - 从仍亳亠仆
def transport =
new HttpTransport("http://example.org:8080/groovy-rc")
def remote = new RemoteControl(transport)
def id = remote {
def user = new User(name: "Me", password: "pwd")
user.save()
user.id
}
Remote control - 从仍亳亠仆
def transport =
new HttpTransport("http://example.org:8080/groovy-rc")
def remote = new RemoteControl(transport)
def id = remote {
def user = new User(name: "Me", password: "pwd")
user.save()
user.id
}
Remote control - 从仍亳亠仆
def transport =
new HttpTransport("http://example.org:8080/groovy-rc")
def remote = new RemoteControl(transport)
def id = remote {
def user = new User(name: "Me", password: "pwd")
user.save()
user.id
}
Remote control - 从仍亳亠仆
def transport =
new HttpTransport("http://example.org:8080/groovy-rc")
def remote = new RemoteControl(transport)
def id = remote {
def user = new User(name: "Me", password: "pwd")
user.save()
user.id
}
亠从舒亳于亶 亠
class LoginSpec extends GebReportingSpec {
def "login"() {
given: "a valid user"
go RegistrationPage
register("user", "password")
logout()
when: "the user logins with valid credentials"
go LoginPage
login("user", "password")
then: "the welcome page is displayed"
at DashboardPage
}
舒亳于亶 亠
class LoginSpec extends GebReportingSpec {
def "login"() {
given: "a valid user"
remote {
SpringUtils.getRegisterService()
.register("user", "password")
}
when: "the user logins with valid credentials"
go LoginPage
login("user", "password")
then: "the welcome page is displayed"
at DashboardPage
亠
弌舒仆亟舒仆亶 Grails 仂亠
弌舒仆亟舒仆亶 Grails 仂亠
Spock 仂亠
丐亠弍仂于舒仆亳
 仂亞亳亠从舒 亞仗亳仂于从舒 亠仂于
 仂亰仄仂亢仆仂 亰舒仗从舒 亞仗仗 亠仂于
 丼亠仍仂于亠从仂亳舒亠仄舒 亟仂从仄亠仆舒亳
亳仄亠 仂亠舒
2014 Jeeconf - Geb Spock
Spock 仂亠
亠亠仆亳亠
 舒亳亳仍亳 Athaydes Spock Reports
 仂弍舒于亳仍亳 亞仗仗亳仂于从
 仂亰仄仂亢仆仂 亰舒仗从舒 亞仗仗
仗亠亳亳从舒亳亶
@Group('Invite program')
@SpecName('Invitee')
class InviteesSpec extends GebBaseSpec {
def "Invitee should get a free payment"() {
when: "user register with invite link"
then: "user has one free payment"
}
}
丼仂 亟舒仍亠?
 丐亠亳仂于舒仆亳亠 于亠从亳
 丕从仂亠仆亳亠 亠仂于
 亠亠仂亟 仆舒 Gherkin (Cucumber) 亟仍
亳亰弍亠亢舒仆亳 亟弍仍亳仂于舒仆亳 从仂仄亠仆舒亳亠于 亳
从仂亟舒
class InviteesSpec extends GebBaseSpec {
def "Invitee should get a free payment"() {
when: "registered with invite link user"
registeredWithInviteLinkUser()
then: "user has one free payment"
to AccountPage
freePayments.text().contains("1")
}
}
仂仗仂?
亠亞仂 仂仂亠亞仂, 亳
仗舒亳弍仂 亰舒 弍!

More Related Content

2014 Jeeconf - Geb Spock