在集成测试期间,Grails null id对约束的错误

时间:2013-03-18 18:45:56

标签: hibernate grails constraints integration-testing grails-2.2

Grails 2.2.0

我正在尝试创建自定义约束以强制用户只有一封主电子邮件。以下是导致错误的简化代码:

用户域类

class User {

    static hasMany = [emails: Email]

    static constraints = {
    }
}

电子邮件域类

class Email {

    static belongsTo = [user: User]
    String emailAddress
    Boolean isMaster

    static constraints = {

        emailAddress unique: ['user']
        isMaster validator: { val, obj ->
            return !val || Email.findByUserAndIsMaster(obj.user, true) == null
        }

    }
}

整合测试

class EmailTests {

    @Before
    void setUp() {

    }

    @After
    void tearDown() {
        // Tear down logic here
    }

    @Test
    void testSomething() {
        def john = (new User(login: 'johnDoe')).save(failOnError: true, flush: true)
        assert (new Email(emailAddress: 'john@gmail.com', user: john, isMaster: true)).save(failOnError: true)
    }
}

运行“grails test-app -integration”将导致:

  

|失败:testSomething(webapp.EmailTests)
  | org.hibernate.AssertionFailure:webapp.Email条目中的null id(发生异常后不刷新会话)       在org.grails.datastore.gorm.GormStaticApi $ _methodMissing_closure2.doCall(GormStaticApi.groovy:105)       在webapp.Email $ __ clinit__closure1_closure2.doCall(Email.groovy:13)       at org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener.onApplicationEvent(AbstractPersistenceEventListener.java:46)       在webapp.EmailTests.testSomething(EmailTests.groovy:21)

如果我将唯一约束更改为在自定义约束之后,则不会发生问题。这里发生了什么?我想了解这里任何相关约束的顺序是什么?

要明确这不会导致问题:

static constraints = {
        isMaster validator: { val, obj ->
            return !val || Email.findByUserAndIsMaster(obj.user, true) == null
        }
        emailAddress unique: ['user']
    }

1 个答案:

答案 0 :(得分:4)

我想我明白了......一对多的关系破裂了。

让我解释一下

  • 测试的第一行创建保存并刷新的User john
  • 第二行创建Email并将john设置为用户属性。

一旦您尝试保存Email实例,GORM就会抱怨。那是因为你将john分配给Email,这是关系的反面。拥有方并不知道这一点,并且在那一点上什么也没有。简单的说。 在添加到用户之前,您无法保存并通过电子邮件发送电子邮件。

这是一种应该有效的测试方法。

void testSomething() {
   def john = new User(login: 'johnDoe')

   john.addToEmails(new Email(emailAddress: 'john@gmail.com', isMaster: true))
   john.save(flush:true)

   assert false == john.errors.hasErrors()
   assert 1 == john.emails.size()  
}

addToEmails()方法将电子邮件实例添加到集合中,并将用户设置在关系的反面。现在这种关系得到满足,保存约翰也应该保存所有电子邮件。


另一条路线

由于问题似乎是Email验证器中对用户实例的引用,我可能还有另一条路线可供您使用。

class User {
    static hasOne = [master: Email]
    static hasMany = [emails: Email]
}

这将消除对有问题的验证器的需求,这使得Email类取决于User进行验证。您可以让用户对他拥有的电子邮件地址以及应该应用的规则负责。 您可以向User添加验证器,以验证您是否拥有电子邮件列表中不存在的主地址,并验证所有分配的地址是否唯一。 例如:

static constraints = {
    master validator: { master, user, errors ->
        if (master.emailAddress in user.emails*.emailAddress) {
            errors.rejectValue('master', 'error.master', 'Master already in e-mails')
            return false
        }
    }

    emails validator: { emails, user, errors ->
        def addresses = emails*.emailAddress
        if (!addresses.equals(emails*.emailAddress.unique())) {
            errors.rejectValue('emails', 'error.emails', 'Non unique e-mail')
            return false
        }
    }
}

我做了一些测试,他们以这种方式做得很好。