Craig Atkinson | Chief Technologist | Object Partners
Behavior-style test framework in Groovy with support for easy data-driven testing
Test cases separated into three main sections
Test class name ends in Spec or Specification and class extends spock.lang.Specification
import spock.lang.Specification
class BankAccountSpec extends Specification {
}
Test case method names can be descriptive sentences
def "when depositing 10 dollars into new account then balance should be 10"() {
}
Much more descriptive than
void depositTestCase1() {
}
class BankAccountSpec extends Specification {
def "when depositing 10 dollars in new account then balance should be 10"() {
given:
BankAccount bankAccount = new BankAccount()
when:
bankAccount.deposit(10)
then:
assert bankAccount.balance == 10
}
}
def "new account should have 0 balance"() {
expect:
assert new BankAccount().balance == 0
}
Run code before each test method
void setup() {
// Setup code goes here
}
Run code after each test method
void cleanup() {
// Cleanup code goes here
}
void setupSpec() {
// Runs once before test methods in class
}
void cleanupSpec() {
// Runs once after test methods in class
}
Run same test body with multiple sets of test inputs and expected outputs
where:
input1 | input2 || output
4 | 6 || 5
12 | 18 || 15
20 | 14 || 17
def 'deposits should increase balance'() {
given:
BankAccount bankAccount = new BankAccount()
when:
bankAccount.deposit(amount)
then:
assert bankAccount.balance == expectedBalance
where:
amount || expectedBalance
10 || 10
25 || 25
50 || 50
}
import spock.lang.Unroll
@Unroll
def 'depositing #amount should increase balance to #expectedBalance'() {
given:
BankAccount bankAccount = new BankAccount()
when:
bankAccount.deposit(amount)
then:
assert bankAccount.balance == expectedBalance
where:
amount || expectedBalance
10 || 10
25 || 25
50 || 50
}
Prints out descriptive and useful failure message when an assertion fails
def 'x plus y equals z'() {
when:
int x = 4
int y = 5
int z = 10
then:
assert x + y == z
}
Condition not satisfied:
x + y == z
| | | | |
4 9 5 | 10
false
class BankAccount {
AuditService auditService
BigDecimal balance
BankAccount() {
auditService = new AuditService()
balance = 0
}
void deposit(BigDecimal amount) {
balance += amount
auditService.record('deposit', amount)
}
}
class BankAccountSpec extends Specification {
def "should record audit event when making deposit"() {
given:
BankAccount bankAccount = new BankAccount()
// Create Spock mock object
AuditService auditService = Mock()
// Use mock object in class-under-test
bankAccount.auditService = auditService
when:
bankAccount.deposit(100)
then:
1 * auditService.record('deposit', 100)
and:
assert bankAccount.balance == 100
}
}
then:
1 * accountService.calculateBalance(account) >> 20
Can use closure to match method arguments
1 * userService.sendWelcomeEmail({ User user ->
user.email == 'jim@test.com' && user.name == 'Jim Smith'
})
1 * userService.sendWelcomeEmail(user) >> {
throw new IllegalStateException()
}
1 * userService.createUser(_, _) >> { String email, String name ->
new User(email: email, name: name)
}
import spock.lang.IgnoreRest
@IgnoreRest
def 'only run this test method'() {
}
Verify that user can sign up
Search for Geb homepage using Google
go "http://www.google.com"
$("input", name: "q").value("Geb")
$("button", name: "btnG").click()
waitFor { $("#search").displayed }
assert $("#search").text().contains("gebish.org")
to GoogleHomePage
searchBox = "Geb"
searchButton.click()
assert searchResults.text().contains("gebish.org")
class GoogleHomePage extends geb.Page {
static url = "http://www.google.com"
static content = {
searchBox { $("input", name: "q") }
searchButton { $("button", name: "btnG") }
searchResults { $("#search") }
}
}
static content = {
depositButtonById { $("#deposit-button") }
depositButtonByClass { $(".deposit-button") }
depositButtonByName { $("input", name: "deposit") }
depositButtonByText { $("input", text: "Deposit") }
}
class LoginPage extends Page {
static url = "/app/login"
}
// In test
LoginPage loginPage = to(LoginPage)
class LoginPage extends Page {
static at = { title == 'Login to my app' }
static url = "/app/login"
}
waitFor {
$("div.alert").displayed
}
assert $("div.alert").text() == "Error creating user"
waitFor {
$("div.message").text() == "Update successful"
}
Can use same test code across different browsers
def seleniumVersion = "2.53.1"
testCompile "org.seleniumhq.selenium:selenium-chrome-driver:${seleniumVersion}"
testCompile "org.seleniumhq.selenium:selenium-firefox-driver:${seleniumVersion}"
testCompile "org.seleniumhq.selenium:selenium-ie-driver:${seleniumVersion}"
GebConfig.groovy
import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.firefox.FirefoxDriver
// Default browser
driver = { new FirefoxDriver() }
environments {
chrome {
// Assumes OS-specific library already downloaded on machine running tests
System.setProperty('webdriver.chrome.driver', '/path/to/chromedriver')
driver = { new ChromeDriver() }
}
}
testCompile("io.github.bonigarcia:webdrivermanager:1.4.1")
import io.github.bonigarcia.wdm.ChromeDriverManager
import org.openqa.selenium.chrome.ChromeDriver
environments {
chrome {
// Downloads driver for the current OS and does all necessary configuration
ChromeDriverManager.getInstance().setup()
driver = { new ChromeDriver() }
}
}
class AccountDepositGebSpec extends geb.spock.GebReportingSpec {
def "should deposit amount into bank account"() {
given:
AccountPage accountPage = to(AccountPage)
DepositPage depositPage = accountPage.clickDepositLink()
when:
depositPage.depositAmount(100)
then:
waitFor { depositPage.successMessage.displayed }
}
}
@Unroll
def "should get error message when logging in with invalid user #username"() {
when: "logging in as invalid user"
LoginPage loginPage = loginAsUser(username)
then: "should show error message"
assert loginPage.errorMessage == expectedErrorMessage
where:
username || expectedErrorMessage
'disabledUser' || 'Sorry, your account is disabled'
'lockedUser' || 'Sorry, your account is locked'
'missingUser' || 'Sorry, we could not find that account'
}
Law School 235 after lunch