覆盖Grails GORM领域类上的事件闭包以进行单元测试

3
我正在处理一个全新的Grails项目,并注意到Spring Security Core生成的User类中默认约定现在通过beforeInsert/Update事件自动对密码进行编码。这是一种漂亮、干净、DRY的编码方式,也使得忘记编码成为不可能。
然而,在试图编写一些利用该User类的单元测试时,我发现我要么必须模拟springSecurityService(因为编码),要么更好地(并且更清楚地)用一个什么都不做的beforeInsert/Update闭包覆盖它。在Groovy中,通常可以使用ExpandoMetaClass覆盖方法,如下:...
User.metaClass.beforeInsert = { /* do nothing */ }  

...但我发现在创建和保存新用户时,原始的beforeInsert继续被调用。这反过来导致我的单元测试失败。对于我来说,只需要简单地解决这个问题并模拟服务即可,但上述应该可以正常工作。我有什么遗漏吗?GORM的事件闭包是否有所不同,我没有注意到?


另外一件需要注意的事情是,我在我的测试中使用了@Mock([User])注解。这可能与奇怪的行为有关,但目前只是一个直觉而已。 - James
实例上是否有expando方法?//问永远不会太晚 :) - Ivar
你为什么认为模拟springSecurityService不是首选的方式呢?我的意思是,当我考虑依赖注入时,首先想到的就是外部化依赖管理,从而能够创建存根/模拟进行测试。在我看来,改变User的metaClass并没有更清晰的方式。 - Mario David
1
@MarioDavid 这不是关于测试的正确方式的问题,那是一个观点问题。这是一个关于为什么覆盖 beforeInsert 不起作用的问题。我只是碰巧使用上述描述来表现这个问题。 - James
3个回答

2
为了提高性能,Grails 直接使用反射与缓存方法句柄来调用事件,而不是使用 Groovy 的元编程层。原因在于,如果您要保存数百个领域实例,则每次事件都需要通过 Groovy 的元编程层会严重影响性能。
有解决这个问题的方法,例如定义自己的 User 类,并根据系统/环境属性禁用基于事件的操作,但目前无法通过元编程覆盖这些方法。

我知道这是一个老问题。有没有办法访问缓存层以覆盖该方法? - christopher

1

beforeInsert 闭包实际上不仅仅是像 toString() 或 save() 这样的方法,而且它是 Gorm 支持的预定义事件。覆盖该方法并不能阻止 Gorm 触发 PreInsert 事件,从而导致原始流程。


2
我知道beforeInsert是一个事件(请看我的最后一句话),但是闭包仅仅决定了在给定模型触发事件时会发生什么。覆盖它应该允许我们像其他任何东西一样改变那个行为。我不是试图阻止GORM触发事件,而是试图改变事件触发时的行为。 - James

0
如果需要的话,您可以将beforeInsert中的代码替换为一个私有方法,然后重写这个私有方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接