Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use Mockito to test that a Java 8 Stream had the expected values?

One of the interactions I want to test is that a class Foo is supposed to pass a Stream<Changes> to FooListener.someChangesHappened. Is there a Mockito idiom to verify that a stream contained the expected objects?

like image 216
Doradus Avatar asked May 07 '16 13:05

Doradus


1 Answers

Assuming you are just verifying the argument to a mock implementation, and are not actually using it, here is a custom Hamcrest Matcher that will get the job done. It gets hairy when you need to read from the Stream more than once, because Streams are not built for that. You'll notice that this solution even needs to protect itself from JUnit calling matches more than once.

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.not;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.verify;

@RunWith(MockitoJUnitRunner.class)
public class Foo {

    @Mock
    FooListener fooListener;

    @Before
    public void happen() {
        fooListener.someChangesHappened(Stream.of(Changes.ONE, Changes.TWO, Changes.THREE));
    }

    @Test
    public void contains() {
        verify(fooListener).someChangesHappened(argThat(streamThat(hasItem(Changes.TWO))));
    }

    @Test
    public void doesNotContain() {
        verify(fooListener).someChangesHappened(argThat(streamThat(not(hasItem(Changes.FOUR)))));
    }

    private static <T> Matcher<Stream<T>> streamThat(Matcher<Iterable<? super T>> toMatch) {
        return new IterableStream<>(toMatch);
    }

    private interface FooListener {
        void someChangesHappened(Stream<Changes> stream);
    }

    private enum Changes {
        ONE, TWO, THREE, FOUR
    }

    private static class IterableStream<T> extends TypeSafeMatcher<Stream<T>> {

        Matcher<Iterable<? super T>> toMatch;
        List<T> input = null;

        public IterableStream(Matcher<Iterable<? super T>> toMatch) {
            this.toMatch = toMatch;
        }

        @Override
        protected synchronized boolean matchesSafely(Stream<T> item) {
            // This is to protect against JUnit calling this more than once
            input = input == null ? item.collect(Collectors.toList()) : input;
            return toMatch.matches(input);
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("stream that represents ");
            toMatch.describeTo(description);
        }
    }
}
like image 155
Yosef Weiner Avatar answered Oct 13 '22 02:10

Yosef Weiner