Sorry if this is a newbie question but I would really appreciate any insights the community could offer with regard to a problem I am having with stubbing the following method which I have in a Grails service, LocationService.
Location locate(String target, String locator, Application app, boolean sync = true) {
if (!target) throw new IllegalArgumentException("Illegal value for msid: " + target)
def locRequest = Request.create(target, Type.LOCATE)
if (!locRequest.save()) {
return Location.error(target, "Error persisting location request")
}
locationSource.locateTarget(target, locator, app, sync)
}
I have a domain class, Request, that as well as the default GORM methods also has some extra domain methods, eg. the create() method below
@EqualsAndHashCode
class Request {
String reference
String msid
Type type
Status status
Destination destination
DateTime dateCreated
DateTime dateCompleted
static create(String msid, Type type, Destination destination = Destination.DEFAULT) {
new Request(reference: reference(type), type: type, status: Status.INITIATED, dateCreated: new DateTime())
}
Finally, I have a Spock specification. I need to mock both the default GORM methods but also some stub some extra domain logic, eg, a static create method, in order to return a valid object to be persisted in the code under test.
Ideally, I would use Spock mocks but I can't use them here as according to the post below from Peter N, they need to be injected into the caller and in this case the Request (which I am trying to mock), is created as a local variable in the locate method in LocationService:
https://groups.google.com/forum/?fromgroups=#!topic/spockframework/JemiKvUiBdo
Nor can I use the Grails 2.x @Mock annotation as, although this will mock the GORM methods, I am unsure if i can mock/stub the additional static create() method from the Request class.
Hence, finally, I have been trying to use the Groovy StubFor / MockFor methods to do this as I believe that these will be used in the call to the test method by wrapping it in a use closure (as below).
Here is the test spec:
@TestFor(LocationService)
// @Mock(Request)
class LocationServiceSpec extends Specification {
@Shared app = "TEST_APP"
@Shared target = "123"
@Shared locator = "999"
def locationService = new LocationService()
LocationSource locationSource = Mock()
def "locating a valid target should default to locating a target synchronously"() {
given:
def stub = new StubFor(Request)
stub.demand.create { target, type -> new Request(msid: target, type: type) }
stub.demand.save { true }
1 * locationSource.locateTarget(target, locator, app, SYNC) >> { Location.create(target, point, cellId, lac) }
def location
when:
stub.use {
location = locationService.locate(target, locator, app)
}
then:
location
}
However, when I run the test, although the stubbed create method returns my Request stub object, I get a failure on the stubbed save method:
groovy.lang.MissingMethodException: No signature of method: com.domain.Request.save() is applicable for argument types: () values: []
Possible solutions: save(), save(boolean), save(java.util.Map), wait(), last(), any()
Could anybody please point out what I am doing wrong here or suggest the best approach to solve my particular case if needing to stub additional methods as well as GORM methods of a domain class that I can't inject directly into the code under test?
Thank you in advance,
Patrick
@Mock
to mock the default methods and how to manually override additional methods. Using@Mock
gave me the former but not the latter. And using StubFor gave me the latter but not the former. But they didn't seem to work in conjunction. However, using@Mock
for the former and modifying the metaClass to get the latter solves the problem perfectly. Many thanks. – Leda