Presentation on the grails build-test-data plugin. Shows how much easier test data can be created, and how much more readable your tests are when you only need to specify the things you actually care about.
See: http://bitbucket.org/tednaleid/grails-test-data/wiki/Home
Blog: http://naleid.com/blog/2009/04/14/grails-build-test-data-01-plugin-released/
5. a book belongs class Book {
String title
static belongsTo = [author: Author]
to an author }
an author has class Author {
String name
many books }
static hasMany = [books: Book]
Tuesday, July 14, 2009
6. class User {
String login
String password
String email
Date birthDate
static constraints = {
login(size: 5..15,
constraints blank: false,
unique: true)
password(size: 5..15,
blank: false)
email(email: true,
blank: false)
birthDate(max: new Date())
}
}
Tuesday, July 14, 2009
7. test data must pass constraints
def user = new User(
login: “tnaleid”,
password: “sekrit1”,
birthDate: new Date() - (365 * 36)
).save()
Tuesday, July 14, 2009
8. whoops! missed an attribute!
def user = new User(
login: “tnaleid”,
password: “sekrit1”,
email: “contact@naleid.com”,
age: new Date() - (365 * 36)
).save()
Tuesday, July 14, 2009
9. constrained attributes are required
even if your test doesn’t care about
them
def user = new User(
login: “tnaleid”,
password: “sekrit”,
email: “contact@naleid.com”,
birthDate: new Date() - (365 * 36)
).save()
// service looks up user in DB by username and deletes it
assertTrue userService.deleteByUsername(“tnaleid”)
Tuesday, July 14, 2009
16. void testTenDollarPurchase() {
def author = new Author(
name: “David Foster Wallace”
).save()
Book book = new Book(
author: author,
title: "The Pale King",
published: new Date(),
price: 10.00 as BigDecimal
).save()
copy/paste/ }
def ord = service.orderBook(book.id)
//... test assertions
modify for every void testTwentyDollarPurchase() {
test method def author = new Author(
name: “David Foster Wallace”
).save()
Book book = new Book(
author: author,
title: "Infinite Jest",
published: new Date(),
price: 20.00 as BigDecimal
).save()
def ord = service.orderBook(book.id)
//... test assertions
}
Tuesday, July 14, 2009
17. def author
void setUp() {
author = new Author(
name: “David Foster Wallace”
).save()
}
void testTenDollarPurchase() {
Book book = new Book(
author: author,
title: "The Pale King",
use setUp method published: new Date(),
price: 10.00 as BigDecimal
).save()
to share objects
def order = service.orderBook(book.id)
across tests }
//... test assertions
void testTwentyDollarPurchase() {
Book book = new Book(
author: author,
title: "Infinite Jest",
published: new Date(),
price: 20.00 as BigDecimal
).save()
def result = service.orderBook(book.id)
//... test assertions
}
Tuesday, July 14, 2009
18. class AuthorObjectMother {
static dfw() {
return new Author(
name: “David Foster Wallace”
).save()
}
}
class BookObjectMother {
static thePaleKing() {
return new Book(
author: AuthorObjectMother.dfw(),
title: “The Pale King”,
published: new Date(),
price: 10.00 as BigDecimal
).save()
object mother }
static infiniteJest() {
return new Book(
author: AuthorObjectMother.dfw(),
pattern title: “Infinite Jest”,
published: new Date(),
price: 20.00 as BigDecimal
).save()
}
}
testTenDollarPurchase() {
Book book = BookObjectMother.thePaleKing()
def result = service.orderBook(book.id)
//... test assertions
}
void testTwentyDollarPurchase() {
Book book = BookObjectMother.infiniteJest()
def result = service.orderBook(book.id)
//... test assertions
}
Tuesday, July 14, 2009
19. // fixtures/dfwBooks.groovy:
fixture {
davidFosterWallace(Author) {
name = “David Foster Wallace”
}
thePaleKing(Book) {
author = davidFosterWallace
title = "The Pale King"
published = new Date()
price = 10.00 as BigDecimal
}
infiniteJest(Book) {
author = davidFosterWallace
title = "Infinite Jest"
published = new Date()
builder DSL / test }
}
price = 20.00 as BigDecimal
fixtures // test class ...
def fixtureLoader
void testTenDollarPurchase() {
fixtureLoader.load("dfwBooks")
Book book = Book.findByTitle(“The Pale King”)
def result = service.orderBook(book.id)
//... test assertions
}
void testTenDollarPurchase() {
fixtureLoader.load("dfwBooks")
Book book = Book.findByTitle(“Infinite Jest”)
def result = service.orderBook(book.id)
//... test assertions
}
Tuesday, July 14, 2009
20. all of these strategies strive
to DRY up your test data
creation
Tuesday, July 14, 2009
21. but all of them repeat the same
rules specified in the constraints
Tuesday, July 14, 2009
22. void testTenDollarPurchase() {
Book book = new Book(
price: 10.00 as BigDecimal
)
mockDomain(Book, [book])
grails does have def order = service.orderBook(book.id)
//...test assertions
mocks ... but what }
are they really void testTwentyDollarPurchase() {
Book book = new Book(
testing? )
price: 20.00 as BigDecimal
mockDomain(Book, [book])
def result = service.orderBook(book)
//... test assertions
}
Tuesday, July 14, 2009
23. void testPurchaseBookService() {
def author = new Author(
name: "First Last",
).save()
Book book = new Book(
what if we could author: author,
title: "title",
published: new Date(),
turn this price: 10.00 as BigDecimal
).save()
def order = service.orderBook(book.id)
//... test assertions
}
void testPurchaseBookService() {
Book book = Book.build(
price: 10.00 as BigDecimal
into this )
def result = service.orderBook(book)
//... test assertions
}
Tuesday, July 14, 2009
24. you can with the
build-test-data plugin
Tuesday, July 14, 2009
32. class Author {
String name
static hasMany = [books: Book]
}
our domain class Book {
String title
static belongsTo = [author: Author]
}
Tuesday, July 14, 2009
33. just give me a def book = Book.build()
assertNotNull book.author
book assertNotNull book.title
Tuesday, July 14, 2009
34. give me a book,
but let me set the def book = Book.build(title:“Infinite Jest”)
title
Tuesday, July 14, 2009
35. let me tweak the def book = Book.build(author:
)
Author.build(name: “Charlie Stross”)
book’s author
Tuesday, July 14, 2009
37. ‣ Domain objects (1..1, 1..N, N..N)
‣ Embedded domain objects
build-test-data ‣
‣
String
Boolean
supports all ‣
‣
Number (Integer, Long, Float, etc)
Byte
known attribute ‣ Date
types ‣
‣
Enum
JodaTime
‣ Any other hibernate persistable class
with a zero-argument constructor
Tuesday, July 14, 2009
38. ‣ nullable
‣ blank
‣ inList
‣ max
‣ maxSize
automatic ‣
‣
min
minSize
constraint support ‣
‣
range
scale
‣ size
‣ url
‣ creditCard
‣ email
Tuesday, July 14, 2009
39. manual constraint ‣
‣
matches (regular expressions)
validator (user-defined constraint)
support ‣ unique
Tuesday, July 14, 2009
44. links
build-test-data home
http://bitbucket.org/tednaleid/grails-test-data/wiki/Home
intro blog post
http://naleid.com/blog/2009/04/14/grails-build-test-data-01-plugin-released/
photo credits
http://www.flickr.com/photos/kaptainkobold/3406169798/in/set-1058884
http://commons.wikimedia.org/wiki/File:Domain_model.png
http://www.flickr.com/photos/nickwheeleroz/2474196275/in/photostream
Tuesday, July 14, 2009