在Grails单元测试中模拟静态get gorm方法

时间:2014-04-28 22:56:09

标签: unit-testing grails mocking

我使用的是Grails 2.3.7。我有一个简单的Person域对象:

class Person {
  String name
  static constraints = {}
}

和控制器:

@Transactional(readOnly = true)
class PersonController {
  def index() {
    render view:'index', model:[personList:Person.list()]
  }

  def show(Long id) {
    def person = Person.get(id)
    render view:'show', model:[person:person]
  }
  ...
}

我试图为控制器编写单元测试以执行show方法,但我不确定如何配置测试以确保静态调用Person.get(id)返回一个值。

这是我的(失败)尝试:

import grails.test.mixin.*
import spock.lang.*

@TestFor(PersonController)
@Mock(Person)
class PersonControllerSpec extends Specification {

  void "Test show action loads Person by id and includes that person in the model rendered"() {
    setup:
      def personMockControl = mockFor(Person)
      personMockControl.demand.static.get() { int id -> new Person(name:"John") }

    when:"A valid person id passed to the show action"
      controller.show(123)

    then:"A model is populated containing the person domain instance loaded by that unique id"
      model.person != null
      model.person.name == "John"
  }
}

此测试失败,因为条件model.person != null失败。如何重写此测试以使其通过?

修改

在这种情况下,我可以通过重新处理测试来支持静态方法调用:

void "Test show action loads Person by id and includes that person in the model rendered"() {
  setup:
    def p = new Person(name:"John").save()

  when:"A valid person id passed to the show action"
    controller.show(p.id)

  then:"A model is populated containing the person domain instance loaded by that unique id"
    model.person != null
    model.person.id == p.id
    model.person.name == "John"
}

但是,我真的很想了解如何正确模拟Person的静态get方法。

编辑2

这是另一种表明我认为需要嘲笑get方法的情况。鉴于此过滤器代码:

def fitlers = {
  buyFilter(controller:"paypal", action:"buy") {
    after = {
      new VendorPayment(payment:request.payment, vendor: Vendor.get(params.buyerId)).save()
    }
  }
}

我尝试使用以下测试测试此过滤器是否按预期工作:

void "test the buyFilter to ensure it creates a VendorPayment when the PayPal plugin controller is called" () {
  setup:
  // how do I mock the Vendor.get(params.buyerId)
  // to return a vendor in order to save a new VendorPayment

  when:
  withFilters(action:"buy") {
    controller.buy()
  }

  then:
  VendorPayment.count() == 1
}

1 个答案:

答案 0 :(得分:11)

就像dmahaptro提到的那样,你不需要嘲笑你的人。通过.get()获取测试中对象的首选/正确方法是您拥有它的方式 - 创建域实例,并将其id传递给控制器​​操作以供使用。

但是,如果你需要模拟这样的静态方法,有两种方法:

1)使用mockFor提供的GrailsUnitTestMixin方法,然后指定静态方法,如下所示:

GrailsMock mockPerson = mockFor(Person)
mockPerson.demand.static.get() { Long id -> new Person(name:"John") }

请注意,在您的情况下,模拟的静态get方法不包含正确的参数类型(使用int而不是Long),因此未调用此方法。 / p>

2)修改课程“metaClass。非常不鼓励更改metaClass(特别是当@Mock注释已经为您设置了所有内容时),并且可能导致测试泄漏(即,在您更改metaClass之后其他测试将无法正常工作,除非您恢复测试完成后的原始元类。)

那就是说,为了熟悉,你嘲笑.get()方法的方法是:

Person.metaClass.static.get = { Long id -> return personWithId }

同样,这被认为是不好的做法,应该避免,但你有它。