one-to-many mapping in grails throwing exception - grails

I am new to Groovy & Grails. I am working on one of the sample one-to-many relationship in Grails.
The below is the code.
class User {
//properties
String login
String password
String role
//constraints and order of display of fields in UI
static constraints = {
login(blank: false, nullable: false, unique: true)
password(blank: false, nullable: false, password: true)
role(inList:["Admin", "Member"])
}
static hasMany = [posts : Post]
}
class Post {
String content
Date dateCreated
static constraints = {
content(blank: true)
}
static belongsTo = [user : User]
}
My Test class in Groovy
#TestFor(User)
class UserTests {
void testUserToPost() {
def user = new User(login: "joe", password: "joe", role:"Admin")
user.addToPosts(new Post(content: "First"));
user.addToPosts(new Post(content: "Second"));
user.addToPosts(new Post(content: "Third"));
user.save(flush: true)
assertEquals 3, User.get(user.id).posts.size()
}
}
While running the test class, getting following exception:
groovy.lang.MissingMethodException: No signature of method: com.library.forum.User.addToPosts() is applicable for argument types: (com.library.forum.Post) values: [com.library.forum.Post : (unsaved)]
Possible solutions: getPosts() at com.library.forum.UserTests.testUserToPost(UserTests.groovy:17)
Can anyone tell me where is the problem in code.

Since Grails 2.1.4, there's a change in mock behavior because of performance issue. So you need to mock all associated entities of the mocked entity.
See GRAILS-9637 - Due to a performance issue, #Mock no longer mocks
associated entities of the mocked entity. These have to be manually
specified. For example the following test will fail in 2.1.4 and
above:
#Mock(Author)
void testAddToBooks() {
def a = new Author()
a.addToBooks(new Book())
}
To correct the above test you need to mock both Author and Book:
#Mock([Author, Book])
void testAddToBooks() {
def a = new Author()
a.addToBooks(new Book())
}
You can check this reference.

You need to mock all related domain classes. Change :
#TestFor(User)
class UserTests {
to
#TestFor(User)
#Mock(Post)
class UserTests {
If you need, the mock annotation support a list of classes, for example: #Mock([Domain1, Domain2, Domain3])

Related

Using shared constraints in Command Objects using grails 3.3

I have a problem migrating my old unit tests from grails 2.5.4 to grails 3.3
I have a lot of commands using shared preferences. An example, the UserCommand inside de UserController:
class UserCommand implements Validateable {
String firstName
static constraints = {
firstName shared: 'nonNullableString'
}
}
In the application.groovy I have defined the constraint this way:
grails.gorm.default.constraints = {
nonNullableString(blank: false, maxSize: 255)
}
Also I have the User domain defined as:
class User {
String firstName
static constraints = {
firstName shared: 'nonNullableString'
}
}
Writing a test I do:
User user = new User()
user.validate()
it works as expected. But when I do this:
UserCommand command = new UserCommand()
command.validate()
It throws an Exception.
`grails.gorm.validation.exceptions.ValidationConfigurationException: Property [User.firstName] references shared constraint [nonNullableString:null], which doesn't exist!`
I thought I made a mistake in the application.groovy file, but shared constraints works for the domains..
Here is a sample project with the same problem: https://github.com/almejo/commandtest
Any thoughts?

Grails Build Test Data Plugin - MissingMethodException when calling DomainObject.build()

The Crux
I am getting the following error in my unit test when calling MyDomainObject.build() via the Build Test Data plugin:
The Exception
groovy.lang.MissingMethodException: No signature of method: us.maponline.pesticide.PesticideProfile.addToApplicators() is applicable for argument types: (us.maponline.pesticide.PesticideApplicator) values: [us.maponline.pesticide.PesticideApplicator : null]
Possible solutions: getApplicators()
at grails.buildtestdata.handler.NullableConstraintHandler.addInstanceToOwningObjectCollection(NullableConstraintHandler.groovy:121)
at grails.buildtestdata.handler.NullableConstraintHandler.populateDomainProperty(NullableConstraintHandler.groovy:88)
at grails.buildtestdata.handler.NullableConstraintHandler.handle(NullableConstraintHandler.groovy:17)
at grails.buildtestdata.DomainInstanceBuilder.createMissingProperty(DomainInstanceBuilder.groovy:187)
at grails.buildtestdata.DomainInstanceBuilder.populateInstance(DomainInstanceBuilder.groovy:147)
at grails.buildtestdata.DomainInstanceBuilder.build(DomainInstanceBuilder.groovy:124)
at grails.buildtestdata.DomainInstanceBuilder.build(DomainInstanceBuilder.groovy:123)
at grails.buildtestdata.BuildTestDataService$_addBuildMethods_closure1.doCall(BuildTestDataService.groovy:25)
at us.maponline.pesticide.PesticideLogServiceTests.testSaveLog(PesticideLogServiceTests.groovy:20)
| Completed 1 unit test, 1 failed in 5289ms
per the stack trace, this is happening within the buildtestdata plugin code. It seems that my class, PesticideApplicator, is null when it is being added to the PesticideProfile class.
How is it possible that the class I'm asking to be built is null when being passed to the PesticideProfile?
Source Code
Test Case
#TestFor(PesticideLogService)
#Build([PesticideLog,PesticideApplicator,PesticideProfile])
class PesticideLogServiceTests
{
void testSaveLog() {
PesticideApplicator applicator = PesticideApplicator.build()
def result = service.createLog(applicator, new Date())
result.errors.each {
log.info "got an error. field = $it.field, message:$it.defaultMessage, rejected value = $it.rejectedValue "
}
assert result: 'no result returned'
assert result.success: 'save failed'
assert result.result instanceof PesticideLog: "result was not PesticideLog"
assert applicator.user.pesticideLogs.size() > 0 : 'expected at least on log to be created.'
}
}
PesticideProfileLog
class PesticideProfile
{
def User user
String companyName
static constraints = {
}
static belongsTo = User
static hasMany = [sites: PesticideSite, applicators: PesticideApplicator]
}
PesticideApplicator
class PesticideApplicator
{
String firstName
String lastName
String company
PesticideApplicatorLicense licenseType
Phone phoneNumber
static belongsTo = [profile:PesticideProfile]
static constraints = {
company blank: false, maxSize: 55
firstName blank: false, maxSize: 55
lastName blank: false, maxSize: 100
phoneNumber nullable: true
}
static mapping = {
licenseType length: 55
}
def getUser(){
profile?.user
}
}
Thanks for all your help!
The issue is caused by the build test data plugin attempting to set the value of the user in the PesticideApplicator. The problem is that getUser() isn't a field, it's just a utility helper:
...
def getUser(){
profile?.user
}
...
Removing getUser() from the PesticideApplicator solved the problem.
That said, I'd still like a helper method to access the user (good to not let my code know about the innards of another class). Marking the method #Transient didn't work; the error still appeared. Short of renaming the method, how can I instruct the build test data plugin to ignore this getter?
Thanks!

Why is GORM performing cascading delete, even if I omit the belongsTo keyword?

I have the following domain classes:
Holiday:
class Holiday {
String justification
User user
//static belongsTo = User
static constraints = {
}
}
User:
class User {
String login
String password
static hasMany = [ holidays : Holiday ]
static constraints = {
}
}
I have created a one-many relationships between Holiday and User. Note that I haven't included belongsTo on the Holiday class. Now I have written the following integration test:
void testWithoutBelongsTo() {
def user1 = new User(login:"anto", password:"secret")
user1.save()
def holiday1 = new Holiday(justification:"went to trip")
holiday1.save()
user1.addToHolidays(holiday1)
assertEquals 1, User.get(user1.id).holidays.size()
user1.delete()
assertFalse User.exists(user1.id)
assertFalse Holiday.exists(holiday1.id)
}
Clearly in the above test case, I'm deleting only the user1 instance, but when I run with assert statements I can see that GORM have implicitly deleted holiday1, too. And my Test cases have PASSED! How this can happen, even though I haven't given belongsTo keyword in Holiday class?
I'm using Grails version 1.3.7.
holiday1 was never saved as it does not validate: property user is neither set nor nullable.
Here is how your code in Holiday.groovy should look like:
class Holiday {
String justification
User user
//static belongsTo = User
static constraints = {
user(nullable: true)
}
}
And your test, with property user set properly in holiday1:
void testWithoutBelongsTo()
{
def user1 = new User(login:"anto", password:"secret")
user1.save(failOnError: true)
def holiday1 = new Holiday(justification:"went to trip",
user: user1) // Set user properly
holiday1.save(failOnError: true)
user1.addToHolidays(holiday1)
assert 1, User.get(user1.id).holidays.size()
holiday1.user = null // Unset user as otherwise your DB
// won't be happy (foreign key missing)
user1.delete()
assert ! User.exists(user1.id)
assert Holiday.exists(holiday1.id)
}
In order to eliminate validation errors quickly in your tests, always use save(failOnError: true). It will throw an exception if your object do not validate.
You should save holiday after adding it to the user.

Overriding dateCreated for testing in Grails

Is there any way I can override the value of dateCreated field in my domain class without turning off auto timestamping?
I need to test controller and I have to provide specific domain objects with specific creation date but GORM seems to override values I provide.
Edit
My classes look like this:
class Message {
String content
String title
User author
Date dateCreated
Date lastUpdated
static hasMany = [comments : Comment]
static constraints = {
content blank: false
author nullable: false
title nullable: false, blank: false
}
static mapping = {
tablePerHierarchy false
tablePerSubclass true
content type: "text"
sort dateCreated: 'desc'
}
}
class BlogMessage extends Message{
static belongsTo = [blog : Blog]
static constraints = {
blog nullable: false
}
}
I'm using console to shorten things up. The problem which I encountered with Victor's approach is, when I write:
Date someValidDate = new Date() - (20*365)
BlogMessage.metaClass.setDateCreated = {
Date d ->
delegate.#dateCreated = someValidDate
}
I get following exception:
groovy.lang.MissingFieldException: No such field: dateCreated for class: pl.net.yuri.league.blog.BlogMessage
When I tried
Message.metaClass.setDateCreated = {
Date d ->
delegate.#dateCreated = someValidDate
}
Script goes well, but unfortunately dateCreated is not being altered.
I was having a similar issue, and was able to overwrite dateCreated for my domain (in a Quartz Job test, so no #TestFor annotation on the Spec, Grails 2.1.0) by
Using the BuildTestData plugin (which we use regularly anyway, it is fantastic)
Double-tapping the domain instance with save(flush:true)
For reference, my test:
import grails.buildtestdata.mixin.Build
import spock.lang.Specification
import groovy.time.TimeCategory
#Build([MyDomain])
class MyJobSpec extends Specification {
MyJob job
def setup() {
job = new MyJob()
}
void "test execute fires my service"() {
given: 'mock service'
MyService myService = Mock()
job.myService = myService
and: 'the domains required to fire the job'
Date fortyMinutesAgo
use(TimeCategory) {
fortyMinutesAgo = 40.minutes.ago
}
MyDomain myDomain = MyDomain.build(stringProperty: 'value')
myDomain.save(flush: true) // save once, let it write dateCreated as it pleases
myDomain.dateCreated = fortyMinutesAgo
myDomain.save(flush: true) // on the double tap we can now persist dateCreated changes
when: 'job is executed'
job.execute()
then: 'my service should be called'
1 * myService.someMethod()
}
}
Getting a hold of the ClosureEventListener allows you to temporarily disable grails timestamping.
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
import org.codehaus.groovy.grails.commons.spring.GrailsWebApplicationContext
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration
import org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventTriggeringInterceptor
import org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventListener
class FluxCapacitorController {
def backToFuture = {
changeTimestamping(new Message(), false)
Message m = new Message()
m.dateCreated = new Date("11/5/1955")
m.save(failOnError: true)
changeTimestamping(new Message(), true)
}
private void changeTimestamping(Object domainObjectInstance, boolean shouldTimestamp) {
GrailsWebApplicationContext applicationContext = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
GrailsAnnotationConfiguration configuration = applicationContext.getBean("&sessionFactory").configuration
ClosureEventTriggeringInterceptor interceptor = configuration.getEventListeners().saveOrUpdateEventListeners[0]
ClosureEventListener listener = interceptor.findEventListener(domainObjectInstance)
listener.shouldTimestamp = shouldTimestamp
}
}
There may be an easier way to get the applicationContext or Hibernate configuration but that worked for me when running the app. It does not work in an integration test, if anyone figures out how to do that let me know.
Update
For Grails 2 use eventTriggeringInterceptor
private void changeTimestamping(Object domainObjectInstance, boolean shouldTimestamp) {
GrailsWebApplicationContext applicationContext = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
ClosureEventTriggeringInterceptor closureInterceptor = applicationContext.getBean("eventTriggeringInterceptor")
HibernateDatastore datastore = closureInterceptor.datastores.values().iterator().next()
EventTriggeringInterceptor interceptor = datastore.getEventTriggeringInterceptor()
ClosureEventListener listener = interceptor.findEventListener(domainObjectInstance)
listener.shouldTimestamp = shouldTimestamp
}
I got this working by simply setting the field. The trick was to do that after the domain object has been saved first. I assume that the dateCreated timestamp is set on save and not on object creation.
Something along these lines
class Message {
String content
Date dateCreated
}
// ... and in test class
def yesterday = new Date() - 1
def m = new Message( content: 'hello world' )
m.save( flush: true )
m.dateCreated = yesterday
m.save( flush: true )
Using Grails 2.3.6
As of Grails 3 and GORM 6 you can tap into AutoTimestampEventListener to execute a Runnable that temporarily ignores all or select timestamps.
The following is a small snippet I use in my integration tests where this is necessary:
void executeWithoutTimestamps(Class domainClass, Closure closure){
ApplicationContext applicationContext = Holders.findApplicationContext()
HibernateDatastore mainBean = applicationContext.getBean(HibernateDatastore)
AutoTimestampEventListener listener = mainBean.getAutoTimestampEventListener()
listener.withoutTimestamps(domainClass, closure)
}
Then in your case you could do the following:
executeWithoutTimestamps(BlogMessage, {
Date someValidDate = new Date() - (20*365)
BlogMessage message = new BlogMessage()
message.dateCreated = someValidDate
message.save(flush: true)
})
I'm using something like this for an initial import/migration.
Taking gabe's post as a starter (which didn't work for me Grails 2.0), and looking at the old source code for ClosureEventTriggeringInterceptor in Grails 1.3.7, I came up with this:
class BootStrap {
private void changeTimestamping(Object domainObjectInstance, boolean shouldTimestamp) {
Mapping m = GrailsDomainBinder.getMapping(domainObjectInstance.getClass())
m.autoTimestamp = shouldTimestamp
}
def init = { servletContext ->
changeTimestamping(new Message(), false)
def fooMessage = new Message()
fooMessage.dateCreated = new Date("11/5/1955")
fooMessage.lastUpdated = new Date()
fooMessage.save(failOnError, true)
changeTimestamping(new Message(), true)
}
}
You can try to disable it by setting autoTimestamp = false in the domain class mapping. I doubt about global overriding because the value is taken directly from System.currentTimeMillis() (I'm looking at org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventListener.java).
So I can only suggest that you override a setter for dateCreated field in your class, and assign your own value. Maybe even metaclass access will work, like
Date stubDateCreated
...
myDomainClass.metaClass.setDateCreated =
{ Date d -> delegate.#dateCreated = stubDateCreated }
I couldn't get the above techniques to work, the call to GrailsDomainBinder.getMapping always returned null???
However...
You can use the fixtures plugin to set the dateCreated property on a domain instance
The initial loading will not do it...
fixture {
// saves to db, but date is set as current date :(
tryDate( SomeDomain, dateCreated: Date.parse( 'yyyy-MM-dd', '2011-12-25') )
}
but if you follow up with a post handler
post {
// updates the date in the database :D
tryDate.dateCreated = Date.parse( 'yyyy-MM-dd', '2011-12-01')
}
Relevant part of the fixtures docs here
AFAIK fixtures don't work for unit testing, although the plugin authors may add unit testing support in the future.
A simpler solution is to use a SQL query in your integration test to set it as you please after you initialize your object with the other values you want.
YourDomainClass.executeUpdate(
"""UPDATE YourDomainClass SET dateCreated = :date
WHERE yourColumn = :something""",
[date:yourDate, something: yourThing])
As of grails 2.5.1, getMapping() method of GrailsDomainBinder class is not static,non of the above method works as is. However, #Volt0's method works with minor tweaking. Since all of us are trying to do so to make our tests working, instead of placing it in BootStrap, I placed it in actual integration test. Here is my tweak to Volt0's method:
def disableAutoTimestamp(Class domainClass) {
Mapping mapping = new GrailsDomainBinder().getMapping(domainClass)
mapping.autoTimestamp = false
}
def enableAutoTimestamp(Class domainClass) {
Mapping mapping = new GrailsDomainBinder().getMapping(domainClass)
mapping.autoTimestamp = true
}
And simply call these methods in tests like
disableAutoTimestamp(Domain.class)
//Your DB calls
enableAutoTimestamp(Domain.class)
The above code can also be placed in src directory and can be called in tests however I placed this in actual test as there was only one class in my app where I needed this.
The easy solution is to add a mapping:
static mapping = {
cache true
autoTimestamp false
}

Grails - testing custom validator on domain class issue

I'm learning grails from Grails - getting started by Jason Rudolph book.
My domain class looks like that:
class Race {
String name;
Date startDateTime
String city
String state
Float distance
Float cost
Integer maxRunners = 10000
static hasMany = [registrations: Registration]
static constraints = {
name(maxSize: 50, blank: false)
startDateTime(validator: {
return it > new Date()
})
city(maxSize: 30, blank: false)
state(inList: ['GA', 'NC', 'SC', 'VA'], blank: false)
distance(min: 3.1f, max: 100f)
cost(min: 0f, max: 999.99f)
}
String toString() { "${this.name} : ${this.city}, ${this.state}" }
}
I want to test the custom validation of startDateTime field. Test looks like that:
class RaceTests extends GrailsUnitTestCase {
protected void setUp() {
super.setUp()
}
protected void tearDown() {
super.tearDown()
}
void testCustomDateValidation() {
def race = new Race()
race.startDateTime = null
assertFalse(race.validate())
}
}
Test looks similar to the one from book I mentioned earlier. But I'm getting
groovy.lang.MissingMethodException: No signature of method: racetrack.Race.validate() is applicable for argument types: () values: []
I'm stuck and didn't find any solution :/ Any help will be appreciated.
You're missing the mockForConstraintsTests() call. The common pattern is to do this in setUp()
protected void setUp() {
super.setUp()
mockForConstraintsTests(Race)
}
For details: http://mrhaki.blogspot.com/2009/04/unit-testing-constraints-in-domain.html
You should not use unit tests or mocking to test domain classes. Grails does create a unit test for domain classes and this should be changed. Move the class to the same package and folder under test/integration and change the base class to GroovyTestCase and you'll have a proper test that runs with the in-memory database and tests persistence, not the mocking framework.

Resources