我使用的是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
}
答案 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 }
同样,这被认为是不好的做法,应该避免,但你有它。