Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grails 3.x - tests from grails 2.5.x don't work #9678

Closed
sting2804 opened this issue Feb 11, 2016 · 15 comments
Closed

Grails 3.x - tests from grails 2.5.x don't work #9678

sting2804 opened this issue Feb 11, 2016 · 15 comments

Comments

@sting2804
Copy link

I have a domain class, which extends non-domain class. When my project was on Grails 2.5.3, the test worked fine.

@Mock(Activity)
class ActivitySpec extends Specification {
    def "test"(){
        expect:
        new Activity(name: "foo").save()
    }
}

And domain

class Activity extends DomainRestResource {

    String name
    String code
    String description
    static hasMany = [....]
    static belongsTo = [... ]

    static constraints = {
        name maxSize: 50
       ....
    }

    static mapping = {
        table name: 'tt_activity'
    }
}

Now i'm upgrading the project to Grails 3.1.1 and everything works fine except all my tests.
Test result on grails 3.1.1:

org.grails.datastore.mapping.model.IllegalMappingException: Mapped identifier [id] for class [com.astaprime.rest.DomainRestResource] is not a valid property

    at org.grails.datastore.mapping.model.config.GormMappingConfigurationStrategy.getIdentity(GormMappingConfigurationStrategy.java:887)
    at org.grails.datastore.mapping.model.AbstractPersistentEntity.resolveIdentifier(AbstractPersistentEntity.java:196)
    at org.grails.datastore.mapping.model.AbstractPersistentEntity.initialize(AbstractPersistentEntity.java:117)
    at org.grails.datastore.mapping.model.AbstractPersistentEntity.getRootEntity(AbstractPersistentEntity.java:221)
    at org.grails.datastore.mapping.model.AbstractMappingContext.initializePersistentEntity(AbstractMappingContext.java:271)
    at org.grails.datastore.mapping.model.AbstractMappingContext.initialize(AbstractMappingContext.java:250)
    at grails.test.mixin.domain.DomainClassUnitTestMixin.initializeMappingContext(DomainClassUnitTestMixin.groovy:93)
    at grails.test.mixin.domain.DomainClassUnitTestMixin.mockDomains(DomainClassUnitTestMixin.groovy:87)
    at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:153)
    at org.spockframework.runtime.model.MethodInfo.invoke(MethodInfo.java:84)
    at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:88)
    at org.spockframework.runtime.extension.builtin.AbstractRuleInterceptor$1.evaluate(AbstractRuleInterceptor.java:37)
    at grails.test.runtime.TestRuntimeJunitAdapter$1$2.evaluate(TestRuntimeJunitAdapter.groovy:46)
    at org.spockframework.runtime.extension.builtin.TestRuleInterceptor.intercept(TestRuleInterceptor.java:38)
    at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:87)
    at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:88)
    at org.spockframework.runtime.extension.builtin.AbstractRuleInterceptor$1.evaluate(AbstractRuleInterceptor.java:37)
    at grails.test.runtime.TestRuntimeJunitAdapter$3$4.evaluate(TestRuntimeJunitAdapter.groovy:73)
    at org.spockframework.runtime.extension.builtin.ClassRuleInterceptor.intercept(ClassRuleInterceptor.java:38)
    at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:87)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

If remove "extends DomainRestResource" from domain, the test succeeds.
I cannot remove it from all my domain classes, it's very important for program logic.
Haw can i fix this? Thanx!

@rpalcolea
Copy link
Contributor

I think the best place to ask these questions is the Grails Slack channel (Questions) http://slack-signup.grails.org/

@sting2804
Copy link
Author

i think it's a bug. But ok, i'll ask this question in Grails Slack channel, thanx

@osscontributor
Copy link
Member

Please show the source code for DomainRestResource and indicate where that source is defined (grails-app/domain/..., src/main/groovy..., etc...).

@osscontributor
Copy link
Member

This isn't really related to your problem but you could simplify the following:

@Mock(Activity)
class ActivitySpec extends Specification {
    def "test"(){
        given:
        Activity activity = new Activity(name: 'dfd').save()
        expect:
        activity
    }
}

To this if you wanted:

@Mock(Activity)
class ActivitySpec extends Specification {
    def "test"(){
        expect:
        new Activity(name: 'dfd').save()
    }
}

@droggo
Copy link
Contributor

droggo commented Feb 11, 2016

Similar issues were reported already and workaround is to change parent class to trait

@sting2804
Copy link
Author

DomainRestResource is defined in src/main/groovy/com/...
DomainRestResource.groovy

abstract class DomainRestResource extends UniversalRestResource {

    @Autowired
    def connectionManager
    @Autowired
    def userActivityService
    @Autowired
    def dataSource

    protected transient int limit
    protected transient int offset
    private transient String tableName

    /*
    some logic
    */
}

And UniversalRestResource.groovy

abstract class UniversalRestResource {

    /*
    some logic
    */

    abstract Object findObjectById(Long id)
    abstract List<Object> findObjectsByQuery(String query, int limit, int offset)
    abstract Long getObjectId()
    abstract String getSelfLink(String resourceName)
    abstract Map parse(String url, GrailsParameterMap params) throws ValidationException
}

@sting2804
Copy link
Author

droggo, can you give a link to one of these issues?

@osscontributor
Copy link
Member

Why are you marking a domain class with @Component?

Does adding @TypeChecked do anything at all given that @CompileStatic is there?

@droggo
Copy link
Contributor

droggo commented Feb 11, 2016

I meant this one: grails/grails-data-mapping#639

@sting2804
Copy link
Author

jeffbrown, The project is not mine, I just help to upgrade it to grails 3.1.1 from 2.5.3 and almost all sources arn't mine. But i already removed this annotation, thanks.

@zyro23
Copy link
Contributor

zyro23 commented Feb 11, 2016

but i think when using a trait instead of an abstract class, there is no way of defining a constraints and/or mapping closure in that trait that would be used by gorm...

@kktec
Copy link

kktec commented Feb 11, 2016

I am seeing the same problem and also did not have this issue on Grails 2.4.4. I have domain classes that extend a non-domain class.

I worked around the problem by adding an "id" property to the base class which is then shadowed by the "id" property in the subclass. This allows the use of the constraints defined in both the base class and in the subclasses.

As others have said, constraints defined in a trait won't be utilized but that would be a nice feature add.

I also believe this is a bug.

The tests I have are all about testing validation/constraints and only use the @testfor annotation, not @mock. I don't see why an assumption should be made at all by the testing infrastructure that a domain class, or any other "validateable" object should have an id property.

@sting2804
Copy link
Author

droggo i cannot use traits instead abstract classes because of a lot of protected methods, and they shouldn't be public. Also a lot of methods overridden by another domains, and these methods marked as @CompileStatic. There will be too many corrections of code sources.

@zyro23
Copy link
Contributor

zyro23 commented Feb 22, 2016

seems the condition that a parent class should only be added to the mapping context if it is not abstract got lost here: grails/grails-data-mapping@3c8b2bc#diff-52a36f8d13b9d1943bbb000840f8fb2aL117

@badbob
Copy link

badbob commented Nov 30, 2016

I have very similar stacktrace

org.grails.datastore.mapping.model.IllegalMappingException: Mapped identifier [id] for class [foo.bar.model.security.UserRole] is not a valid property
	at org.grails.datastore.mapping.model.config.GormMappingConfigurationStrategy.getIdentity(GormMappingConfigurationStrategy.java:807)
	at org.grails.datastore.mapping.model.AbstractPersistentEntity.resolveIdentifier(AbstractPersistentEntity.java:125)
	at org.grails.datastore.mapping.model.AbstractPersistentEntity.initialize(AbstractPersistentEntity.java:88)
	at org.grails.datastore.mapping.model.AbstractMappingContext.initializePersistentEntity(AbstractMappingContext.java:250)
	at org.grails.datastore.mapping.model.AbstractMappingContext.addPersistentEntities(AbstractMappingContext.java:183)
	at grails.test.mixin.domain.DomainClassUnitTestMixin.mockDomains(DomainClassUnitTestMixin.groovy:86)
	at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:153)
	at org.spockframework.runtime.model.MethodInfo.invoke(MethodInfo.java:84)
	at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:88)
	at org.spockframework.runtime.extension.builtin.AbstractRuleInterceptor$1.evaluate(AbstractRuleInterceptor.java:37)
	at grails.test.runtime.TestRuntimeJunitAdapter$1$2.evaluate(TestRuntimeJunitAdapter.groovy:49)
	at org.spockframework.runtime.extension.builtin.TestRuleInterceptor.intercept(TestRuleInterceptor.java:38)
	at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:87)
	at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:88)
	at org.spockframework.runtime.extension.builtin.AbstractRuleInterceptor$1.evaluate(AbstractRuleInterceptor.java:37)
	at grails.test.runtime.TestRuntimeJunitAdapter$3$4.evaluate(TestRuntimeJunitAdapter.groovy:76)
	at org.spockframework.runtime.extension.builtin.ClassRuleInterceptor.intercept(ClassRuleInterceptor.java:38)
	at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:87)

But, entity UserRole has no super class. And it has composite key:

package foo.bar.model.security;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity @Table(name="user_role")
public class UserRole implements Serializable {

	private static final long serialVersionUID = -7733009390177318889L;

	@Id	
	@ManyToOne(optional=false)
	@JoinColumn(name="user_id")
	private User user;
	
	@Id 
	@ManyToOne(optional=false)
	@JoinColumn(name="role_id")
	private Role role;

	public UserRole() {}
	
	public UserRole(User user, Role role) {
		this.user = user;
		this.role = role;
	}
	
	public User getUser() {
		return user;
	}

	public void setUser(User user) {
		this.user = user;
	}

	public Role getRole() {
		return role;
	}

	public void setRole(Role role) {
		this.role = role;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof UserRole)) {
			return false;
		}
		
		UserRole other = (UserRole) obj;
		return (user != null 
				&& role != null 
				&& other.user != null
				&& other.role != null
				&&  user.equals(other.user) 
				&&  role.equals(other.role))
			|| super.equals(obj);
	}

    @Override
    public int hashCode() {
	int result = 37;
	if (user != null && user.getId() != null) {
	    result = result*17 + user.getId().intValue();
	}
	if (role != null && role.getId() != null) {
	    result = result*17 + role.getId().intValue();
	}
	return result;
    }
}

I suppose, that problem is in org.grails.datastore.mapping.model.MappingFactory:

public IdentityMapping createDefaultIdentityMapping(final ClassMapping classMapping) {
        return new IdentityMapping() {

            public String[] getIdentifierName() {
                return new String[] { IDENTITY_PROPERTY };
            }

            public ClassMapping getClassMapping() {
                return classMapping;
            }

            public Property getMappedForm() {
                // no custom mapping
                return null;
            }
        };
    }

The value of const IDENTITY_PROPERTY = "id".
But there is no such property in my entity.
I think, that better solution wold be using of hashCode() method, instead of "id" member value in this case.

Grails version: 2.5.4
grails-datastore-gorm version: 3.1.5-RELEASE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants