Using Spock to stub both Gorm and other methods in a Grails domain class

grails, grails-orm, mocking, spock, stub

Solution

I believe you should be able to use Grails' `@Mock` annotation like you mentioned for the GORM methods, and then you will need to manually mock the static methods:

@TestFor(LocationService)
@Mock(Request)// This will mock the GORM methods, as you suggested
class LocationServiceSpec extends Specification {
...
    void setup() {
        Request.metaClass.static.create = { String msid, Type type, Destination destination = Destination.DEFAULT ->
             //Some logic here
        }
    }
...

When using the `@Mock` annotation, Grails will mock the default methods (save/get/dynamic finders), but it doesn't do anything to any additional methods you may have added, so you need to manually mock those.

Problem

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

Original source