2

I have this setup (simplified):

import java.util.function.Function;

public class Foo {
    Bar bar;

    void myMethod() {
        final Function<String, String> reference;
        if (...)
            reference = String::toLowerCase;
        else
            reference = String::toUpperCase;

        this.bar.otherMethod(reference);
    }
}

class Bar {
    void otherMethod(final Function<String, String> transform) {
        /* ... */
    }
}

I would like to verify the "myMethod" behavior.

I have tried to mock the bar instance, call the myMethod method and verify(bar).otherMethod(expectedReference)

Unfortunately this approach fails, mostly because - as described in https://stackoverflow.com/a/38963341/273593 - the method reference is compiled to a new instance of an anonimous class.

Is there some other way to check that the correct reference has been passed to bar.otherMethod(...)? Keep in mind that myMethod doesn't call the reference itself (nor the otherMethod... the variable is passed around for 2-3 nested calls).

Vito De Tullio
  • 2,332
  • 3
  • 33
  • 52
  • You could assign the references to class fields in the constructor, then you'd have a fixed value you can verify against. – daniu May 27 '20 at 12:06
  • 1
    Unit testing should verify that a method provides the specified *end result*, not that it has a particular implementation detail like calling another method with a predetermined argument. – Holger May 27 '20 at 15:00
  • @Holger so, how do you test a void method? – Vito De Tullio May 28 '20 at 07:27
  • 2
    Every method has some kind of result, otherwise, you don’t need to call it. When its purpose is to alter runtime data structures, verify that the objects have been changed as required. If it is supposed to write to a file, verify the file, should it alter a database, check the database. – Holger May 28 '20 at 07:37
  • In general I could concur with you. but in this specific case the only reason of this method is literally to "choose" between two method references and delegate the actual work to bar.otherMethod... My goal was to test the condition of the choose – Vito De Tullio May 28 '20 at 10:27

1 Answers1

3

You could mock Bar and use an ArgumentCaptor to capture the value passed to the otherMethod and then assert that it's the expected reference. A simple test class may look like this (imports omitted for brevity):

@RunWith(MockitoJUnitRunner.class)
class MyTest {
    @Mock
    private Bar bar;
    @InjectMocks
    private Foo foo;
    @Captor
    private ArgumentCaptor<Function<String, String>> captor;

    @Test
    public void test() {
        // insert your arguments here if you have any
        foo.myMethod();
        // verify bar is called and capture the method reference
        verify(bar).otherMethod(captor.capture());
        Function<String, String> transform = captor.getValue();
        // do some assertions here, just checking that String::toLowerCase was used
        // you may change this to fit your needs
        Assert.assertEquals("somevalue", transform.apply("SomEvALuE"));
    }
}

The whole snippet above is simplified of course, because you provided a simplified example. Also: you may not be able to use @InjectMocks because you need to inject Bar via another way into Foo.

Lino
  • 19,604
  • 6
  • 47
  • 65
  • Yeah, at the end I found the same workaround (calling the reference "by hand" and understanding what was from the behavior). Still, I hoped for a more straightforward (and explicit approach) – Vito De Tullio May 28 '20 at 07:31
  • @VitoDeTullio that is the only way I currently know of, given your example, that lets you test the result. Because you're working with `void` methods you have to test the side effects, in that case simply that a method is called with the arguments you're expecting. – Lino May 28 '20 at 09:32