5

In class MyClass that I test I have:

public void execute(){
    service.call(ThisClass::method1);
}

And following:

void method1(){do 1;}
void method2(){do 2;}

And in test:

@Mock
Service service;

@Test
public void testCallMethod1()
{
     MyClass myClass = new MyClass();
     myClass.execute();

     service.verify(any(Runnable.class));
}

And it works, but, how do I verify that parameter instead of any Runnable was method1 and not method2?

I'm looking for solution that will look like (For example, not really works):

service.verify(eq(MyClass::method1.getRunnable()))
Anton
  • 1,409
  • 1
  • 19
  • 37
  • What do you mean by `how do I specify that parameter was ...` If you need to know which method was called, this might not be the best implementation – AxelH Nov 09 '16 at 12:25
  • Which instead of that, sorry for my poor English, editing the question to clarify. – Anton Nov 09 '16 at 12:26
  • 2
    Ok, this is just for testing purposes... missed that part. This is a bit strange to test this because the result is important, the way to achieve it is a bit hard to check. Without any update in the `method#()`, I doubt there is a way. – AxelH Nov 09 '16 at 12:39
  • I’m not using Mockito, but as far as I understand, there is no reason to verify that a particular argument has been passed to `call`, as the program logic to verify, is, that a (mocked) `service.call`’s invocation of the `run()` method will end up in the intended (mocked) `method1()` or `method2()`. That should be possible. The equality of method references, however, is entirely unspecified and trying to work-around it is a waste of resources. After all, the program logic wouldn’t change, if the caller uses an anonymous inner class instead of the method reference. – Holger Nov 09 '16 at 12:44
  • @Holger, to clarify methods 1 and 2 are not mocked, but in the same class that is under test. My reasoning was, if I can verify(mock.method(eq(7))), why can't I verify (mock.method(eq(this::specific::method)))? If you say that it's unspecified, you mean that JVM decides about equality of method references? Functions (Methods) are almost first-class citizens now in java 8, am I right? – Anton Nov 09 '16 at 12:50
  • 2
    You can say that functions are almost first-class citizens, but the functional interface implementations created for them have an unspecified equality. Likewise, value types might become first class citizens in a future version, but the object identity of their boxed representation will be unspecified. The outcome of `Integer.valueOf(0xcafebabe) == Integer.valueOf(0xcafebabe)` is also unspecified. That doesn’t mean that `Integer` objects aren’t first class citizens… – Holger Nov 09 '16 at 12:53
  • Thank you for the explanation. I've tried to run following tests: http://pastebin.com/CzF3ghsU, and seen that equals will work for me. But I have no idea if it's permitted or specified to get equals() method on Runnable. – Anton Nov 09 '16 at 13:00
  • For `Integer` objects, `equals` is well defined, for the objects created for lambda expressions and method references, it isn’t. In Oracle’s JRE & OpenJDK, these objects do not override `Object.equals`, hence, it has the same result as the object identity test `==`, whose result is intentionally unspecified. See the quote at the end of [this answer](http://stackoverflow.com/a/27524543/2711488)… – Holger Nov 09 '16 at 15:06
  • 2
    Can't you call `run` on the `Runnable` and verify it has the desired effect? Then you are testing an outcome rather than an implementation detail – David Rawson Nov 09 '16 at 19:38

1 Answers1

1

The following works for me:

public class MyTest {

public static class X{
    static void method1() {};
    static void method2() {};
}

@Test
public void throwAway() {
  ExecutorService service = mock(ExecutorService.class);
  Runnable command1 = X::method1;
  Runnable command2 = X::method2;
  service.execute(command1);
  verify(service).execute(command1);
  verify(service, never()).execute(command2);
}

}

The key was extracting the method reference lambda and using it in both the execution and the verify. Otherwise, each "::" operator produces a distinct lambda instance which doesn't work with equality testing, as some of the comments above discussed the semantics of lambda equality.

Kevin Welker
  • 7,719
  • 1
  • 40
  • 56