Search code examples
mockitojava-stream

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?


Solution

  • 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);
            }
        }
    }