Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mockito test java 8 lambda Consumer API

I need to test a class that is using another interface as a dependency injection with a lambda Consumer.

@Builder
public class Interactor {

    private final Gateway gateway;

    void process(String message, Consumer<String> response){
        gateway.process(message, uuid -> {
            response.accept(uuid.toString());
        });
    }
}

The dependency is defined like so:

public interface Gateway {

    void process(String message, Consumer<UUID> uuid);
}

How would I mock the Gateway so I can provide a UUID value response to my test?

This is what I have tried so far:

@Test
void whillReturnTheInjectedUUIDValueTest() {

    UUID uuid = UUID.randomUUID();

    Gateway gateway = Mockito.mock(Gateway.class);
    Mockito.when(gateway.process("ignored", uuid1 -> {return uuid;}));

    Interactor.builder().gateway(gateway).build().process("ignored", s -> {
        Assertions.assertEquals(uuid.toString(), s);
    });
}

How should I provide the return value to the consumer?

like image 671
Gadi Avatar asked May 06 '18 11:05

Gadi


2 Answers

For now I ended up mocking my own inner class without Mockito.

class InteractorTest {

    @Test
    void whillReturnTheInjectedUUIDValueTest() {

        UUID uuid = UUID.randomUUID();

        Gateway gateway = GatewayMock.builder().value(uuid).build();

        Interactor.builder().gateway(gateway).build().process("ignored", s -> {
            Assertions.assertEquals(uuid.toString(), s);
        });
    }

    @Builder
    private static class GatewayMock implements Gateway {

        private final UUID value;

        @Override
        public void process(String message, Consumer<UUID> uuid) {
            uuid.accept(value);
        }

    }
}
like image 178
Gadi Avatar answered Nov 15 '22 02:11

Gadi


What you want...

is to verify the result of the logic of Interactor#process, which takes a Consumer<String> and basically just calls Gateway#process with a Consumer<UUID> implementation that calls back to the Consumer<String> parameter.

Ideally you want to do this just by calling Interactor#process and verifying the call to the Consumer<String> you supply.

Your problem is...

that it is up to the implementator of Gateway to trigger the Consumer<UUID> (and consequently the Consumer<String>). So you need to inject an implementation of Gateway that does just that.

The solution is...

along the lines of the answer you provided (i.e. using a Fake Object instead of a Mock), unless there is a real implementation of Gateway in your production code that you can easily use instead (but I assume you'd be doing that already if that was the case).

Can you do it with Mockito? Sure, you can call methods on a mock object's method arguments, as shown in this answer: https://stackoverflow.com/a/16819818/775138 - but why would you go there if you can provide a sufficient Gateway implementation in a single line:

Gateway gw = (message, consumer) -> consumer.accept(uuid);

where uuid is the UUID instance that you constructed before. So no need to declare a static inner class just for this.

However...

there is an important issue with your assertion: if Interactor does nothing at all instead of calling the Gateway, the Consumer<String> you provide (containing the assertion) won't get triggered, so your test will pass! Just assign the value to a member variable of your test class, and assert on that outside the lambda.

like image 24
herman Avatar answered Nov 15 '22 02:11

herman