如何正确地将 grails 单元测试帮助程序方法移动到单独的文件中



我有一些常用的单元测试辅助方法放在一个单独的文件中。例如,这个想法是允许我的XYZTests.groovy调用TestHelper.getUserObject(),以获得一个完全初始化的用户实例。

现在的问题是,在用户的beforeInsert()中调用了一个springSecurityService.encodePassword(pw),它总是失败,因为在TestHelper.groovy中没有springSecurityService的模拟

java.lang.NullPointerException: Cannot invoke method encodePassword() on null object

User.groovy中:

def beforeInsert() {
    // ...
    password = springSecurityService.encodePassword(pw)
    // ...
}

注意:我想避免在TestHelper.groovy中进行任何嘲笑,以便在集成测试中使用它的方法。

尽管如此,即使我尝试在TestHelper.groovy的任何地方调用mockFor()我也得到了一个MME:

No signature of method: static myproject.TestHelper.mockFor() is applicable for argument types: (java.lang.Class, java.lang.Boolean) values: [class grails.plugins.springsecurity.SpringSecurityService, true]
groovy.lang.MissingMethodException: No signature of method: static myproject.TestHelper.mockFor() is applicable for argument types: (java.lang.Class, java.lang.Boolean) values: [class grails.plugins.springsecurity.SpringSecurityService, true]
    at myproject.TestHelper.mockSpringSecurityService(TestHelper.groovy:59)
    at myproject.TestHelper$mockSpringSecurityService.callStatic(Unknown Source)
    at myproject.TestHelper.getUserObject(TestHelper.groovy:47)
    at myproject.TestHelper$getUserObject.call(Unknown Source)
    at myproject.UserTests.setUp(UserTests.groovy:26)

注意:我目前像这样嘲笑springSecurityService.encodePassword

// in UserTests.groovy
protected void setUp() {
    // mockDomain(...) and such here
    def u = TestHelper.getUserObject("Pummel")
    u.springSecurityService = mockSpringSecurityService()
    assert u.save()
}
private mockSpringSecurityService() {
    def ssService = mockFor(SpringSecurityService,true)
    ssService.metaClass.encodePassword() { password ->
        "08a2d3c63bf9fc88276d97a9e8df5f841fd772724ad10f119f7e516f228b74c6"
    }
    ssService
}
  1. 关于我如何使用帮助程序类同时仅在单元测试中保留所有模拟的任何想法?
  2. 我最好在哪里放置TestHelper.groovy文件,以便在集成和单元测试中使用它?

请注意,当我将所有帮助程序直接移动到UserTests.groovy时,一切正常!

解决方案是避免调用 TestHelper.groovy 中的任何user.save()

这是有道理的,因为对于许多(单元)测试,无论如何都不需要持久化(保存)实例。

另一方面,许多情况实际上需要未保存的意图。(例如,为了测试.save()本身的某些效果)

集成测试的一个工作示例是:

def user = TestHelper.getUserObject()
user.save()

对于单元测试:

def user = TestHelper.getUserObject()
user.springSecurityService = new SpringSecurityService() // or the described mock accordingly
user.save()

这样可以将任何模拟排除在TestHelper.groovy之外

  1. 在你的TestHelper中,你可以使用Groovy ExpandoMetaClass metaClass.sta在SpringSecurityService上为encodePassword打一个模拟闭包:

    SpringSecurityService.metaClass.'static'.encodePassword = {'08a2d3c63bf9fc88276d97a9e8df5f841fd772724ad10f119f7e516f228b74c6'}
    
  2. 我会把这个类放在 src/groovy 下的测试包中