Unable to mock Grails Service method when unit tes

2020-07-17 08:51发布

问题:

Am getting the following error message when testing the controller - see below for code. How can I correct this? When I invoke the service method from the controller (run-app) and it works fine.

Exception:

groovy.lang.MissingMethodException: No signature of method: grails.test.GrailsMock.isOk() is applicable for argument types: (java.lang.String) values: [H] at ...VControllerSpec.test something(VControllerSpec.groovy:)

class: VControllerSpec

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(VController)
@Mock(VService)
class VControllerSpec extends Specification {

    void "test something"() {
        given:
        def vServiceMock = mockFor(VService)
        vServiceMock.demand.isOk { String yeah -> return true }
        controller.vService = vServiceMock.createMock()

        when:
        def isO = vServiceMock.isOk("H") 

        then:
        isO == true     
    }
}

class:VService

import grails.transaction.Transactional

@Transactional
class VService {
    def isOk = { String yeah ->     
        def isO = false
        return isO
    }
}

Thanks, Steve

回答1:

Assuming there is an action in VController as:

def myAction() {
    vService.isOk('Hello')
}

below test should pass

void 'test service'() {
    given:
    def vServiceMock = mockFor(FormatService)
    vServiceMock.demand.isOk { String yeah -> return true }
    controller.vService = vServiceMock.createMock()

    when:
    def isO = controller.myAction() 

    then:
    isO == true
}

There are few things to optimize here including using a method isOk instead of a closure as best practices.



回答2:

One is not expected to test a method which is being mocked. When we mock a method, we just assume its implementation is correct and has already been tested (in some other unit test). The purpose of mocking is to limit our focus of testing to limited lines of code (mostly commonly one method), in your case the your controller action. So the above test case could have been written as:

Assuming your action is like this:

def myAction(){
 [iso: vServiceMock.isOk()] // assuming isOk returns boolean true
}

void "test myAction"() {
        given:
        def vServiceMock = mockFor(VService)
        vServiceMock.demand.isOk { String yeah -> return true }
        controller.vService = vServiceMock.createMock()

        when:
        def model = controller.myAction() 

        then:
        model.iso   //you can skip comparison with true 
    }