【问题标题】:Grails 2.4.4 : How do mock a transient service inside a domain?Grails 2.4.4:如何模拟域内的瞬态服务?
【发布时间】:2015-03-02 17:53:33
【问题描述】:

我已经尝试解决这个问题 2 天了,我真的很困惑和沮丧。我有一个带有用于自定义验证的服务的域对象。域如下所示:

class Llama { 
String name
transient myFetcherService

static transients = [
            'myFetcherService'
    ]

static constraints = {
        name validator: { val, obj ->
            if (obj.nameExists(val) == true) {
                //return some error here.
            }
        }
    }

protected boolean nameExists(String name) {
        List<Llama> llamasList = myFetcherService.fetchExistingLlamasByName(name)

        if (llamasList.isEmpty()) {
            return false
        }

        return true
    }
}

现在,我有另一个服务,它只是保存 Llama 对象的列表。它看起来像这样:

class LlamaFactoryService {

   public void createLlamas(List<String> llamaNames) {
     llamaNames.each { name ->
         new Llama(name: name).save()
     }
   }

}

在我的测试中。我不断收到此错误:

Failure:  createLlamas should create Llammas (com.myLlamaProject.LlamaFactoryServiceSpec)
|  java.lang.NullPointerException: Cannot invoke method myFetcherService on null object

我不明白。在我的测试中,在“给定”部分中为服务添加了一个元类。当它尝试保存时,它表明该服务为空。这是我的测试的样子:

given:
def myFetcherService = mockFor(MyFetcherService)   
myFetcherService.demand.fetchExistingLlamasByName {def name -> return []}
Llama.metaClass.myFetcherService = myFetcherService.createMock()

when:
service.createLlamas(['Pablo','Juan','Carlos'])

then:
//some validations here....

我还尝试在方法nameExists() 上使用metaClass,例如:

Llama.metaClass.myFetcherService = { def name -> false }

,但它给了我与上面相同的 nullPointerException。有人能指出我正确的方向吗?我有点卡住了。 :(

提前感谢您的阅读和帮助。

【问题讨论】:

  • 无论是单元测试还是集成测试都在这里添加相关标签。
  • 这是一个单元测试.. 谢谢

标签: unit-testing grails testing


【解决方案1】:

您正在使用单元测试,单元测试的一般规则是通常不会为您创建 bean,因此您需要自己注入它们。

(编辑代码以反映我误读了问题的事实) 我认为你想要一个类似的测试模式:

given:
def mockMyFetcherService = Mock(MyFetcherService) // create the mock
Llama.metaClass.getMyFetcherService = { mockMyFetcherService } // inject the dependency
def returnList = []  // let's just define this here and you can re-use this pattern in other tests more easily

when:
service.createLlamas(['Pablo','Juan','Carlos'])

then:
// tell Spock what you expect to have happen with your Mock - return the List you defined above
3 * mockFetcherService.fetchExistingLlamasByName(_) >> returnList

如果将服务注入元类不起作用 (suggested here),您始终可以尝试在单元测试本身 (http://www.block-consult.com/blog/2011/08/17/inject-spring-security-service-into-domain-class-for-controller-unit-testing/) 中使用 defineBeans{} 闭包。

所以你可以试试:

defineBeans {
  myFetcherService(MockMyFetcherService)
}

其中MockMyFetcherService 在定义测试的同一文件中定义。这是followed here的做法:

有关更多 Spock 交互的示例,请参阅 here

如果您使用的是 Grails 2.4.3 或更低版本,您需要将 CGLIB 放入 BuildConfig.groovy 但我看到 here 已经在 2.4.4 中为您完成了,所以您应该可以使用Mock(classname).

【讨论】:

  • 也适用于 2.3.11。
  • 那么,我在使用域的服务中注入了 MyFetcherService 吗?不在 MyFetcherService 所在的域中?从来没有想过...我正在使用grails 2.4.4。我会试试这个。感谢您的回复。
  • 行不通。 myFetcherService 在域中使用,而不是在服务中使用。我得到了一个 .MissingPropertyException: No such property: myFetcherService for class LlamaFactoryService。 :\
  • 对不起,我的错误,我误读了问题,并认为您使用的是服务内的服务,而不是域内的服务。让我稍后再给您另一个建议。
  • @CancerMan 希望新的建议有用,我很感兴趣,因为我还在 Grails 中学习。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-20
  • 2012-01-22
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-02
相关资源
最近更新 更多