Unit testing 如何在动态搭建的Grails控制器的集成测试中模拟服务?

Unit testing 如何在动态搭建的Grails控制器的集成测试中模拟服务?,unit-testing,grails,mocking,Unit Testing,Grails,Mocking,我试图在一个动态搭建的控制器的集成测试中模拟一个服务。我收到一个错误,指示无法从测试访问服务的控制器属性 看起来动态搭建的控制器根本无法通过单元测试进行测试,所以我使用的是集成测试。我想模拟服务来测试我的应用程序中的错误处理。这是Grails2.2.0中的一个bug还是我只是做错了 grails测试应用程序的结果是: groovy.lang.MissingPropertyException: No such property: myService for class: MyController

我试图在一个动态搭建的控制器的集成测试中模拟一个服务。我收到一个错误,指示无法从测试访问服务的控制器属性

看起来动态搭建的控制器根本无法通过单元测试进行测试,所以我使用的是集成测试。我想模拟服务来测试我的应用程序中的错误处理。这是Grails2.2.0中的一个bug还是我只是做错了

grails测试应用程序的结果是:

groovy.lang.MissingPropertyException: No such property: myService for class: MyController
例如:

我已经修改了
src/templates/scaffolding/Controller.groovy

class ${className}Controller {
  MyService myService

  def action() {
    render myService.serviceMethod()
  }
}
class MyController {
    static scaffold = MyDomainClass
}
class MyControllerTests extends GroovyTestCase {
  def myController

  @Before
  void setUp() {
    myController = new MyController()
  }

  void testMock() {
    myController.myService = [ serviceMethod : { return "foo" } ] as MyService
    controller.action()
  }
}
动态搭建
MyController.groovy

class ${className}Controller {
  MyService myService

  def action() {
    render myService.serviceMethod()
  }
}
class MyController {
    static scaffold = MyDomainClass
}
class MyControllerTests extends GroovyTestCase {
  def myController

  @Before
  void setUp() {
    myController = new MyController()
  }

  void testMock() {
    myController.myService = [ serviceMethod : { return "foo" } ] as MyService
    controller.action()
  }
}
集成测试
MyControllerTests.groovy

class ${className}Controller {
  MyService myService

  def action() {
    render myService.serviceMethod()
  }
}
class MyController {
    static scaffold = MyDomainClass
}
class MyControllerTests extends GroovyTestCase {
  def myController

  @Before
  void setUp() {
    myController = new MyController()
  }

  void testMock() {
    myController.myService = [ serviceMethod : { return "foo" } ] as MyService
    controller.action()
  }
}

尝试使用setter方法:

void testMock() {
    myController.setMyService([ serviceMethod : { return "foo" } ])
    controller.action()
}

如果执行:
println c.metaClass.methods*.name
,您将看到有像getSetMyService()和getGetMyService()这样的方法。我不确定,但Grails可能不是在添加字段,而是在为字段get/set方法添加getter。

集成测试应该如下所示实现。如果我们在测试中模拟服务,我们必须自己重置它。Grails并没有为我们做这件事,这很神秘,因为控制器是在
setUp()
中创建的

上面droggo的回答揭示了在SUT中注入模拟的正确方法。我还将添加一个使用Groovy模拟的示例。不过有点冗长

class MyControllerTests extends GroovyTestCase {
  def myController
  def myService

  @Before
  void setUp() {
    myController = new MyController()
  }

  @After
  void tearDown() {
    myController.setMyService(myService)
  }

  void testMapMock() {
    myController.setMyService([ serviceMethod : { return "foo" } ] as MyService)
    controller.action()
  }

  void testGroovyMock() {
    def myServiceMockContext = new StubFor(MyService)
    myServiceMockContext.demand.serviceMethod() { -> return "bar" }
    def myService = myServiceMockContext.proxyInstance()
    controller.setMyService(myService)
    controller.action()
  }
}

谢谢setXxxService()方法成功了!作为参数的映射需要类型转换:
controller.setMyService([…]作为MyService)
。此外,您需要在下一次测试中将服务重置为真实服务。Grails似乎在mock设置完成后仍保留着它。我将在下面的另一个答案中展示一个示例实现。