【问题标题】:Overriding service method in grails integration test在 Grails 集成测试中覆盖服务方法
【发布时间】:2012-12-07 13:42:39
【问题描述】:

我正在尝试在集成测试中测试控制器的操作。这是一个简单的场景,我试图测试的操作是调用服务的方法。我正在尝试使用元类覆盖该方法,但它看起来不起作用,即服务的真实方法总是被调用,而不是我使用元类覆盖的方法。我在这里做错了什么?

这是控制器的方法:

class MyController {
  MyService myService

  def methodA() {
    def u = myService.find(params.paramA)
    render view: "profile", model:  [viewed: u]
  }

这是我实现集成测试的方式:

class MyControllerTests extends GroovyTestCase {

 MyController controller

 void testMethodA() {
    controller = new MyController()

    // Mock the service
    MyService mockService = new MyService()
    mockService.getMetaClass().find = { String s ->
      []
    }

    controller = new MyController()
    controller.myService = myService

    controller.methodA()
  }

附:我在 STS 2.9.2 中使用 grails 2.0.0

【问题讨论】:

    标签: testing grails groovy metaprogramming integration-testing


    【解决方案1】:

    首先我推荐使用Spock Framework,这是一个非常好的测试库,除了integrates with Grails pretty well。 您的测试将如下所示:

    @TestFor(MyController) // TestFor is builtin Grails annotation
    class MyControllerSpec extends Specification {
    
        // thanks to TestFor annotation you already have 'controller' variable in scope
    
        MyService mockService = Mock(MyService)
    
        // setup method is executed before each test method (known as feature method)
        def setup() {
            controller.myService = mockService
        }
    
        def 'short description of feature being tested'() {
            given:
            mockService.find(_) >> [] // this tells mock to return empty list for any parameter passed
    
            when:
            controller.methodA()
    
            then:
            // here goes boolean statements (asserts), example:
            controller.response.text.contains 'Found no results'
        }
    }
    

    如果您更喜欢不使用 Spock,则对于模拟,您需要最简单的方法是使用 Groovy 强制转换。看看这个:

    MyService mockService = [find: { String s -> [] }] as MyService
    

    这是地图强制。在您模拟单个方法的情况下,甚至不需要 Map ,因此您可以更简单地编写它。

    MyService mockService = { String s -> [] } as MyService
    

    这是强制关闭。好吧,指定参数也不是必需的,因为您没有处理它。

    MyService mockService = { [] } as MyService
    

    最后一条语句基本上意味着 mockService 上调用的任何方法都将执行指定的闭包,因此结果中将返回空列表。

    越简单越好,干杯!

    顺便说一句,在使用 Spock 时,您仍然可以使用强制模拟。 Spock 模拟(使用 Mock() 方法创建)对于测试更高级的案例很有用,例如交互。

    更新:对于集成测试,您将扩展 IntegrationSpec,并且不需要使用 @TestFor。

    【讨论】:

    • topr,非常感谢!我最终使用了您建议的强制技术,一切正常。
    • 不是集成测试的问题吗?看起来代码是用于单元测试的。
    【解决方案2】:

    我建议您使用 Grails 注释来模拟您的服务,如下例取自文档10.1 Unit Testing:

    @Mock([Book, Author, BookService])
    

    然后测试控制器看起来像:

    void testSearch() {
          def control = mockFor(SearchService)
          control.demand.searchWeb { String q -> ['mock results'] }
          control.demand.static.logResults { List results ->  }
          controller.searchService = control.createMock()
          controller.search()      assert controller.response.text.contains "Found 1 results"
    }
    

    【讨论】:

      猜你喜欢
      • 2023-03-31
      • 1970-01-01
      • 2013-01-03
      • 1970-01-01
      • 2016-06-15
      • 2017-11-22
      • 1970-01-01
      • 2014-11-03
      • 1970-01-01
      相关资源
      最近更新 更多