18. Validation
‣ Create only a valid bean
‣ 3 validation ways
‣ Check explicitly for validation
joe.validate()
19. Validation
‣ Create only a valid bean
‣ 3 validation ways
‣ Check explicitly for validation
joe.validate()
‣ Save a catch exception
joe.save(failOnError:true)
20. Validation
‣ Create only a valid bean
‣ 3 validation ways
‣ Check explicitly for validation
joe.validate()
‣ Save a catch exception
joe.save(failOnError:true)
‣ Save a check for non-null return
assert joe.save()
25. Dynamic finders (for single return)
‣ Domain class definition generate static methods
def p = Person.findByUsername(‘lucky_luke’)
def p = Person.findByFirstName(‘Lucky’)
def p = Person.findByFirstNameAndLastName(‘Joe’, ‘Dalton’)
26. Dynamic finders (for single return)
‣ Domain class definition generate static methods
def p = Person.findByUsername(‘lucky_luke’)
def p = Person.findByFirstName(‘Lucky’)
def p = Person.findByFirstNameAndLastName(‘Joe’, ‘Dalton’)
‣ Multiple results => returns first (sorted on id)
def p = Person.findByLastName(‘Dalton’)
27. Dynamic finders (for single return)
‣ Domain class definition generate static methods
def p = Person.findByUsername(‘lucky_luke’)
def p = Person.findByFirstName(‘Lucky’)
def p = Person.findByFirstNameAndLastName(‘Joe’, ‘Dalton’)
‣ Multiple results => returns first (sorted on id)
def p = Person.findByLastName(‘Dalton’)
‣ findByXxxx efficient with unique:true fields
28. Update
‣ Update: change fields values and save into database
29. Update
‣ Update: change fields values and save into database
1. modify bean as usual
30. Update
‣ Update: change fields values and save into database
1. modify bean as usual
2. validate/save as for creation
33. Explicit controller
‣ It is possible to generate scaffold controller code
generate-controller eop.lec11.twitter.Person
34. Explicit controller
‣ It is possible to generate scaffold controller code
generate-controller eop.lec11.twitter.Person
‣ PersonController.groovy write operation & test
35. Explicit controller
‣ It is possible to generate scaffold controller code
generate-controller eop.lec11.twitter.Person
‣ PersonController.groovy write operation & test
‣ For example, read:
def show = {
def personInstance = Person.get(params.id)
if (!personInstance) {
flash.message = "${message(code:
'default.not.found.message', .....)}"
redirect(action: "list")
}
else {
[personInstance: personInstance]
}
}
40. Integration tests
‣ Resides under test/integration/
PersonIntegrationTests.groovy
‣ Launched with
test-app -integration
41. Integration tests
‣ Resides under test/integration/
PersonIntegrationTests.groovy
‣ Launched with
test-app -integration
‣ Results:
- summary on the console output (count success/failures)
- html files under target/tests-reports/html
- plain text files under target/tests-reports/html
- failure summary available
- stdout/stderr accessible for each test case
43. Faster grails command
‣ Launch command (<alt><ctrl>G) interactive
‣ On the console, enter command
test-app -integration
44. Faster grails command
‣ Launch command (<alt><ctrl>G) interactive
‣ On the console, enter command
test-app -integration
‣ Hit enter to relaunch last command
45. Faster grails command
‣ Launch command (<alt><ctrl>G) interactive
‣ On the console, enter command
test-app -integration
‣ Hit enter to relaunch last command
‣ After several commands, PermGenException can occur
- terminate
- relaunch interactive
51. mockDomain
‣ It is possible to make some unit testing with domain
‣ No real database is connected, but a fake layer
52. mockDomain
‣ It is possible to make some unit testing with domain
‣ No real database is connected, but a fake layer
‣ In each method (not setup())
mockDomain(Person)
mockDomain(Person, initialBeanList)
53. mockDomain
‣ It is possible to make some unit testing with domain
‣ No real database is connected, but a fake layer
‣ In each method (not setup())
mockDomain(Person)
mockDomain(Person, initialBeanList)
‣ All single domain CRUD (and more) operations possible
54. mockDomain example
void testDelete(){
//buildDaltonFamily() return a list of 4 Person
mockDomain(Person, buildDaltonFamily())
assert Person.count() == 4
Person p=Person.findByUsername('joe_dalton')
assertNotNull p
p.delete()
// we should only have 3 members left
assert Person.count() == 3
p=Person.findByUsername('joe_dalton')
assertNull p
}
55. mockDomain limits
‣ No explicit database operation (hibernate criteria, HQL) are
possible
56. mockDomain limits
‣ No explicit database operation (hibernate criteria, HQL) are
possible
‣ Multiple domain class interaction are fully possible (cf.
relationships)
57. mockDomain limits
‣ No explicit database operation (hibernate criteria, HQL) are
possible
‣ Multiple domain class interaction are fully possible (cf.
relationships)
‣ Connection with data already entered in a database
61. Message domain
create-domain-class Domain
‣ Just a text (String) and a commiter (Person)
class Message {
String text
Person commiter
static constraints = {
text(size:1..140, blank:false)
commiter(nullable:false)
}
}
62. Message + Person
‣ Attach two messages to a user
Person joe=Person.findByUsername('joe_dalton')
new Message(text:'hello', commiter:joe).save()
new Message(text:'world', commiter:joe).save()
63. Message + Person
‣ Attach two messages to a user
Person joe=Person.findByUsername('joe_dalton')
new Message(text:'hello', commiter:joe).save()
new Message(text:'world', commiter:joe).save()
‣ Look for message from joe
Message.findAllByCommiter(joe)
64. Message + Person
‣ Attach two messages to a user
Person joe=Person.findByUsername('joe_dalton')
new Message(text:'hello', commiter:joe).save()
new Message(text:'world', commiter:joe).save()
‣ Look for message from joe
Message.findAllByCommiter(joe)
‣ Not possible to access to message directly from joe bean
- one solution: explicitly declare setCommiter(Person p) in
Message.groovy that would add the message to a list in joe;
- problem for deletion, save inconsistency...
69. One-to-many relationship (cont’d)
‣ Add a message:
joe.addToMessages(new Message(text:‘hello world’)).save()
‣ Will execute the following actions
- create a message with joe as commiter
- save the message
- add the message to joe’s list
71. One-to-many relationship (cont’d)
‣ Access to the list
joe.messages
‣ Deleting will cascade
joe.delete()
- all messages with joe as commiter will also be deleted
72. Testing
‣ Test database consistency with two domain: integration testing
// taken from MessageIntegrationTests.groovy
// 4 Person are added in the setup() method
public void testListMessagesUserDeletion(){
Person joe=Person.findByUsername('joe_dalton')
Person averell=Person.findByUsername('averell_dalton')
joe.addToMessages(new Message(text:'hello world')).save()
joe.addToMessages(new Message(text:'i'm running')).save()
averell.addToMessages(new Message(text:'i'm eating')).save()
assert Message.count() == 3
assert Person.count() == 4
joe.delete()
assert Person.count() == 3
//having deleted joe should delete all message related to joe
assert Message.count() == 1
}