New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't mock interface default methods #729

Open
chrylis opened this Issue May 15, 2017 · 2 comments

Comments

Projects
None yet
2 participants
@chrylis

chrylis commented May 15, 2017

I have an interface that has a default method overload that can transform an instance of any sort of bar (in this case, message format) into a canonical format for processing.

interface Foo {
  void doThing(SpecificType bar);

  default void doThing(SuperType bar) {
    doThing(new SpecificType(bar));
  }
}

I want to test that when I call Foo#doThing with a SomeOtherBar, the message gets correctly transformed and sent out the SpecificType overload. However, Spock's Mock() intercepts all methods, including default methods.

Since one main purpose of default methods is to support adaptation onto legacy APIs, this behavior will prevent backwards-compatibility bridges from working if a provider API is upgraded. Instead, it should be possible to either by default (my preference) or explicitly exclude default interface methods from being implemented by the mock proxy.

@szpak

This comment has been minimized.

Show comment
Hide comment
@szpak

szpak May 15, 2017

Contributor

You could use a mechanism usually used in Spy() to instruct Spock to call a real method on the underlying object.

given:
    Foo foo = Mock()
    foo.doThingDefault(_) >> { callRealMethod() }

However, in the situation where both methods have the same name the syntax is more complicated as Spock takes the first stubbing for the same method name, even with a different input argument type. Therefore, you need to use something like:

given:
    Foo foo = Mock()
    foo.doThing(_) >> { it[0] instanceof String ? it[0] : callRealMethod() }

(to get know why you can't return just it, but it[0] you can check my presentation from Gr8Conf 2016 :) ).

It works fine with Spock 1.1 which migrated to ByteBuddy (I haven't tested it with cglib).

Btw, I'm not sure if it is worth to have an ability to disable mocking default methods globally.

Contributor

szpak commented May 15, 2017

You could use a mechanism usually used in Spy() to instruct Spock to call a real method on the underlying object.

given:
    Foo foo = Mock()
    foo.doThingDefault(_) >> { callRealMethod() }

However, in the situation where both methods have the same name the syntax is more complicated as Spock takes the first stubbing for the same method name, even with a different input argument type. Therefore, you need to use something like:

given:
    Foo foo = Mock()
    foo.doThing(_) >> { it[0] instanceof String ? it[0] : callRealMethod() }

(to get know why you can't return just it, but it[0] you can check my presentation from Gr8Conf 2016 :) ).

It works fine with Spock 1.1 which migrated to ByteBuddy (I haven't tested it with cglib).

Btw, I'm not sure if it is worth to have an ability to disable mocking default methods globally.

@chrylis

This comment has been minimized.

Show comment
Hide comment
@chrylis

chrylis May 17, 2017

I'm aware of the "first stub" issue, and it feels like a bug (specifically, that it happens even if I explicitly say doThing(_ as SpecificType)). And I've been using Spock for a while and reflexively dance around the it semantics now. ;-)

Why would it not be worth it? The rule is unambiguous, default methods are easy to identify, and there are numerous instances, such as with the Collection types retrofit, where checking that the type under test Does The Right Thing when a default method does some sort of wrapping or delegation is immensely useful, especially for regression control where you start out using a general-purpose unoptimized default implementation and then later come back to add a custom one.

chrylis commented May 17, 2017

I'm aware of the "first stub" issue, and it feels like a bug (specifically, that it happens even if I explicitly say doThing(_ as SpecificType)). And I've been using Spock for a while and reflexively dance around the it semantics now. ;-)

Why would it not be worth it? The rule is unambiguous, default methods are easy to identify, and there are numerous instances, such as with the Collection types retrofit, where checking that the type under test Does The Right Thing when a default method does some sort of wrapping or delegation is immensely useful, especially for regression control where you start out using a general-purpose unoptimized default implementation and then later come back to add a custom one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment