I'm using Grails 2.3.7. I have a simple Person domain object:
class Person {
String name
static constraints = {}
}
And controller:
@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]
}
...
}
I'm trying to write a unit test for the controller to exercise the show
method, but I am not sure how I need to configure the test to ensure that the static call Person.get(id)
returns a value.
Here's my (failing) attempt:
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"
}
}
This test fails because the condition model.person != null
fails. How do I rewrite this test in order to make it pass?
EDIT
In this case I can side-step the static method call by reworking the test thusly:
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"
}
However, I'd really like to understand how to correctly mock the Person
's static get
method.
EDIT 2
Here's another situation to demonstrate my perceived need to mock the get
method. Given this filter code:
def fitlers = {
buyFilter(controller:"paypal", action:"buy") {
after = {
new VendorPayment(payment:request.payment, vendor: Vendor.get(params.buyerId)).save()
}
}
}
I'm trying to test that this filter works as expected using the following test:
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
}
Like dmahaptro mentioned, you do not need to mock your person. The preferred/proper way of grabbing an object in a test via .get()
is the way you have it--you create the domain instance, and pass its id to the controller action for consumption.
However, if you need to mock a static method like this there are two approaches:
1) Use the mockFor
method provided by GrailsUnitTestMixin
and then specify the static method like so:
GrailsMock mockPerson = mockFor(Person)
mockPerson.demand.static.get() { Long id -> new Person(name:"John") }
Note that in your case, the mock's static get
method did not contain the correct parameter type (int
was used instead of Long
) so this method was not being invoked.
2) Modify the class' metaClass
. Changing the metaClass is highly discouraged (especially when the @Mock
annotation already sets everything up for you), and can lead to test leakage (i.e., other tests will not work properly after you've changed the metaClass, unless you restore the original metaclass after the test is done).
That said, for the sake of familiarity, the way you would mock the .get()
method is via:
Person.metaClass.static.get = { Long id -> return personWithId }
Again, this is considered bad practice and should be avoided, but there you have it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With