Extract from class CUT under test:
def compileOutputLines( TopDocs topDocs ) {
println "gubbins"
}
Test code:
def "my feature"(){
given:
CUT stubCut = Stub( CUT ){
compileOutputLines(_) >> { TopDocs mockTD ->
// NB no Exception is thrown
// try {
println "babbles"
callRealMethod()
println "bubbles"
// }catch( Exception e ) {
// println "exception $e"
// }
}
}
CUT spyCut = Spy( CUT ){
compileOutputLines(_) >> { TopDocs mockTD ->
println "babbles 2"
callRealMethod()
println "bubbles 2"
}
}
when:
stubCut.compileOutputLines( Mock( TopDocs ))
spyCut.compileOutputLines( Mock( TopDocs ))
then:
true
}
Output to stdout:
babbles
bubbles
babbles 2
gubbins
bubbles 2
I tried to find a link online to the full Spock Framework Javadoc... but I couldn't find it... the "non-framework" Javadoc is here, but you won't find the method callRealMethod in the index.
From the Javadoc API I have generated locally from the source, I can indeed find this method: it is a method of org.spockframework.mock.IMockInvocation. It says:
java.lang.Object callRealMethod()
Delegates this method invocation to the real object underlying this
mock object, including any method arguments. If this mock object has
no underlying real object, a CannotInvokeRealMethodException is
thrown.
Returns:
the return value of the method to which this invocation was delegated
My understanding (such as it is) is that a Stub should cause this Exception to be thrown. But it doesn't appear to be. Any comment from a passing expert?
Preface
This is an interesting question. In theory my answer would be:
callRealMethod() is only available for spies, not for mocks or stubs. It is also only mentioned in the chapter about spies, did you notice?
Think about it: A spy wraps a real object, so there you have a reference to a real method you can call. The same is not true for mocks and stubs which are just no-op subclasses. If you could call a real method for a stub, it would be a spy.
The puzzle
In reality I am seeing a different, even weirder behaviour from yours in my own test with Spock 1.1 (Groovy 2.4): No matter if I use a mock, stub or spy, callRealMethod() always calls the real method. This is really a surprise. So, yes, the behaviour is different from what I would have expected. Looking through the interface implementation's source code while debugging, I also cannot see any checks for the type of mock object (is it a spy or not?). The real method is just identified and called.
The solution
Looking at class DynamicProxyMockInterceptorAdapter I found the explanation for this behaviour: The exception mentioned in the IMockInvocation Javadoc is only thrown when trying to call the real method for an interface type mock, never for mocks or class type objects:
public Object invoke(Object target, Method method, Object[] arguments) throws Throwable {
IResponseGenerator realMethodInvoker = (ReflectionUtil.isDefault(method) || ReflectionUtil.isObjectMethod(method))
? new DefaultMethodInvoker(target, method, arguments)
: new FailingRealMethodInvoker("Cannot invoke real method '" + method.getName() + "' on interface based mock object");
return interceptor.intercept(target, method, arguments, realMethodInvoker);
}
So the sentence "if this mock object has no underlying real object, a (...)Exception is thrown" is in essence correct, but ambiguous because it does not explain what "underlying real object" means. Your assumption was just wrong, so was mine. Lesson learned for both of us.
Now when would you see the described behaviour?
package de.scrum_master.stackoverflow;
public interface MyInterface {
void doSomething();
}
package de.scrum_master.stackoverflow
import org.spockframework.mock.CannotInvokeRealMethodException
import spock.lang.Specification
class MyInterfaceTest extends Specification {
def "Try to call real method on interface mock"() {
given:
MyInterface myInterface = Mock() {
doSomething() >> { callRealMethod() }
}
when:
myInterface.doSomething()
then:
thrown(CannotInvokeRealMethodException)
}
def "Try to call real method on interface stub"() {
given:
MyInterface myInterface = Stub() {
doSomething() >> { callRealMethod() }
}
when:
myInterface.doSomething()
then:
thrown(CannotInvokeRealMethodException)
}
def "Try to call real method on interface spy"() {
given:
MyInterface myInterface = Spy() {
doSomething() >> { callRealMethod() }
}
when:
myInterface.doSomething()
then:
thrown(CannotInvokeRealMethodException)
}
}
Update: I have just created issue #830 requesting improvements in Spock's documentation.
Related
I have an abstract service:
abstract class ParentService {
abstract Map someMethod() throws NumberFormatException
}
And another service that extends above class:
class ChildService extends ParentService {
#Override
Map someMethod() throws NumberFormatException {
//Business Logic
}
}
I want to mock someMethod() using groovy metaClass. I need to mock this method for writing test cases for ChildService. This is what I have done for mocking:
ChildService.metaClass.someMethod = { -> "Mocked method" }
But this is not working and the call always executes actual method from the service. What needs to be done here? Am I missing something?
Please Note that I have to mock just one method not the entire service.
Maybe you could just mock method on instance?
def child = new ChildService()
child.metaClass.someMethod = { -> "Mocked method" }
I have an integration test where I sometimes want to mock the return of a service method. However, I have seen that once I mock that method, the subsequent tests that call it will also use the mocked function.
Is this normal? If so, how can I have test which sometimes use mocked functions and sometimes use the real implementation?
Here is my code:
MyController {
def someService
def save(){
...
def val = someService.methodToMock()//sometimes want to mock other times, not
...
}
}
MyTest {
def "test 1"(){
...
//I want to mock here
myController.someService.metaClass.methodToMock = { [] }
...
myController.save()
}
def "test 2"(){
...
//I don't want to mock here, however
// it is returning the mocked results
myController.save()
}
}
In general you don't want to change anything to do with metaclasses in integration or functional tests, only in unit tests. It's expected that you'll be doing this in unit tests and there's automatic support for restoring the original metaclass after each test or after each test class runs depending on the version of Grails and how things are configured. But this isn't the case in integration tests.
There are several different approaches you can use. If you use untyped dependency injection, e.g. def someService, then you can overwrite the real service instance with anything you want, and as long as it has the method(s) that you'll be invoking during the test method the controller won't know or care that it's not the real service.
I like to use a map of closures in this case, since Groovy will invoke a closure as if it were a method. So for 'test 1' you could do this:
def "test 1"() {
...
def mockedService = [methodToMock: { args -> return ... }]
myController.someService = mockedService
...
myController.save()
}
This works because you get a new instance of the controller for each test, and you change the service just for that instance, but the real service isn't affected at all.
Your controller invokes someService.methodToMock(), which is actually someService.get('methodToMock').call(), but the map access and closure invocation syntax can take advantage of Groovy's syntactic sugar to look like a regular method call.
Another option is to subclass the service and override the method(s) that you want, and replace the injected instance with that. This or something like it would be necessary if you type the dependency injection (e.g. SomeService someService). Either create a named subclass (class TestSomeService extends SomeService { ... }) or create an anonymous inner class:
def "test 1"() {
...
def mockedService = new SomeService() {
def methodToMock(args) {
return ...
}
}
myController.someService = mockedService
...
myController.save()
}
Altering the metaClass in one test will absolutely affect other tests. You're altering the groovy system, and need to perform some special cleanup if you're metaClassing. At the end my methods where I metaClass, I call a function to revoke the metaClass changes, passing in the name of the class that was metaClassed, and the instance metaClassed if there was one.
def "some authenticated method test"() {
given:
def user = new UserDomain(blah blah blah)
controller.metaClass.getAuthenticatedUser = { return user }
when:
controller.authenticatedMethod() // which references the authenticated user
then:
// validate the results
cleanup:
revokeMetaClassChanges(theControllerClass, controller)
}
private def revokeMetaClassChanges(def type, def instance = null) {
GroovySystem.metaClassRegistry.removeMetaClass(type)
if (instance != null) {
instance.metaClass = null
}
}
Alternatively, you can just mock the service in the test. A method similar to that mentioned by Burt could be:
def "some test"() {
given:
def mockSomeService = mockFor(SomeService)
mockSomeService.demand.methodToMock(1) { def args ->
return []
}
controller.someService = mockSomeService.createMock()
when:
controller.save()
then:
// implement your validations/assertions
}
I have a service method which in-turn calls another method from it, mutiple times.
def methodA(){
....
methodB()
....
}
Is there a way to write an integration test, which could test the number of times the "methodB()" is called?
It can be done using unit test, but can it be done in integration?
Here's a regular JUnit 3 test, but the approach would work fine in a Spock test.
You can use the &. operator to get a reference to a method and call the method via the reference, so I would do that and then replace the method in the metaclass with a closure that increments a counter and then calls the real method using the saved reference.
class FooServiceTests extends GroovyTestCase {
def fooService
void testCountMethodB() {
def methodB = fooService.&methodB
int count = 0
fooService.metaClass.methodB = { ->
count++
methodB()
}
fooService.methodA()
assert count == 5 // or whatever number you expect
}
}
If methodB has arguments, be sure to register a closure with the same signature, e.g.
fooService.metaClass.methodB = { int bar, String wahoo ->
count++
methodB(bar, wahoo)
}
Within a Spock unit test, I am trying to test the behaviour of a method findRepositoriesByUsername independent of getGithubUrlForPath, both belonging to the same service.
Repeated attempts to use the metaClass have failed:
String.metaClass.blarg produces an error No such property: blarg for class: java.lang.String
service.metaClass.getGithubUrlForPath to modify the service instance doesn't work
GithubService.metaClass.getGithubUrlForPath to modify the service class doesn't work
Tried adding/modifying methods on the metaClass in the test methods' setup and when blocks, neither worked as expected
The test:
package grails.woot
import grails.test.mixin.TestFor
#TestFor(GithubService)
class GithubServiceSpec extends spock.lang.Specification {
def 'metaClass test'() {
when:
String.metaClass.blarg = { ->
'brainf***'
}
then:
'some string'.blarg == 'brainf***'
}
def 'can find repositories for the given username'() {
given:
def username = 'username'
def requestPathParts
when: 'the service is called to retrieve JSON'
service.metaClass.getGithubUrlForPath = { pathParts ->
requestPathParts = pathParts
}
service.findRepositoriesByUsername(username)
then: 'the correct path parts are used'
requestPathParts == ['users', username, 'repos']
}
}
The service:
package grails.woot
import grails.converters.JSON
class GithubService {
def apiHost = 'https://api.github.com/'
def findRepositoriesByUsername(username) {
try{
JSON.parse(getGithubUrlForPath('users', username, 'repos').text)
} catch (FileNotFoundException ex) {
// user not found
}
}
def getGithubUrlForPath(String ... pathParts) {
"${apiHost}${pathParts.join('/')}".toURL()
}
}
I've tested the String.metaClass.blarg example in the groovy shell (launched by grails), and it did as expected.
Do I have a fundamental misunderstanding here? What am I doing wrong? Is there a better way to handle the desired test (replacing a method on the service under test)?
This is how the tests can be written to make them pass:
def 'metaClass test'() {
given:
String.metaClass.blarg = { -> 'brainf***' }
expect:
// note blarg is a method on String metaClass
// not a field, invoke the method
'some string'.blarg() == 'brainf***'
}
def 'can find repositories for the given username'() {
given:
def username = 'username'
def requestPathParts
when: 'the service is called to retrieve JSON'
service.metaClass.getGithubUrlForPath = { String... pathParts ->
requestPathParts = pathParts
[text: 'blah'] // mimicing URL class
}
service.findRepositoriesByUsername(username)
then: 'the correct path parts are used'
requestPathParts == ['users', username, 'repos']
}
Why don't you use Spock's great Mocking abilities?
Look at http://spockframework.github.io/spock/docs/1.0/interaction_based_testing.html#_creating_mock_objects
There is no need to peek inside metaclass itself, you can create some stub object, and demanded method will be called instead of original one. Also you can use Groovy's MockFor and StubFor, they can be a little bit easier.
You cannot fully trust metaclass inside spock tests specification.
There is some complex logic inside it, which can easyly mess thing's up. Try run some tests under debugger, and you will see it.
Spock uses metaclasses under the hood. It can override your own try's.
I have some Java stuff like this:
public interface EventBus{
void fireEvent(GwtEvent<?> event);
}
public class SaveCommentEvent extends GwtEvent<?>{
private finalComment oldComment;
private final Comment newComment;
public SaveCommentEvent(Comment oldComment,Comment newComment){
this.oldComment=oldComment;
this.newComment=newComment;
}
public Comment getOldComment(){...}
public Comment getNewComment(){...}
}
and test code like this:
def "...."(){
EventBus eventBus=Mock()
Comment oldComment=Mock()
Comment newCommnet=Mock()
when:
eventBus.fireEvent(new SaveCommentEvent(oldComment,newComment))
then:
1*eventBus.fireEvent(
{
it.source.getClass()==SaveCommentEvent;
it.oldComment==oldComment;
it.newComment==newComment
}
)
}
I want to verify that the eventBus.fireEvent(..) gets called once with an Event with type SaveCommentEvent and construction parameters oldComment and newComment.
Code runs without errors but problem is:
After changing closure stuff from
{
it.source.getClass()==SaveCommentEvent;
it.oldComment==oldComment; //old==old
it.newComment==newComment //new==new
}
To
{
it.source.getClass()==Other_Class_Literal;
it.oldComment==newComment; //old==new
it.newComment==oldComment //new==old
}
Still, code runs without error? Apparently the closure didn't do what I want, so the question is: How to do argument capturing?
I got it:
SaveCommentEvent firedEvent
given:
...
when:
....
then:
1 * eventBus.fireEvent(_) >> {arguments -> firedEvent=arguments[0]}
firedEvent instanceof SaveModelEvent
firedEvent.newModel == newModel
firedEvent.oldModel == oldModel
then:
1*eventBus.fireEvent(
{
it.source.getClass()==SaveCommentEvent;
it.oldComment==oldComment;
it.newComment==newComment
}
)
In your code it is a Groovy Closure Implicit Variable reference to a mock eventBus Interface which has no fields. How could you verify them?
Also, I think the order of events that has to happen to use Spock Mocks is not necessarily intuitive. I would write it up here except it would not be as good as Kenneth Kousen's explanation.
Same idea with #Alex Luya but put the assertions in the closure and use assert on each of them. cf. Spock Framework Reference Documentation.
then:
1 * eventBus.fireEvent(_) >> {
def firedEvent = it[0]
assert firedEvent instanceof SaveModelEvent
assert firedEvent.newModel == newModel
assert firedEvent.oldModel == oldModel
}
In 2021 (7 yrs later) it is possible to do the following with groovy (2.5):
...
then:
1 * eventBus.fireEvent(_) >> { SaveModelEvent event ->
assert event.newModel == newModel
assert event.oldModel == oldModel
}
0 * _
.. which feels more handy to me and saves a line or two. :)
If you want to mock a method's response and also verify the same method's params(same as capturing the params), you can use Spock's code constraints (among other constraints) to partially match params, and at the same time, verify the method params. :
1 * list.add({
verifyAll(it, Person) {
firstname == 'William'
lastname == 'Kirk'
age == 45
}
}) >> mockedResponse
PS: Solution inspired by this response from #Leonard Brünings
In the answer from #alex-luya above, I found that the variable firedEvent needs the #Shared annotation.
Then I can capture the value and run my checks on the value outside the closure.