5

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?

Doradus
  • 938
  • 1
  • 9
  • 15
  • 2
    you might want to rethink the way you write test methods. Tests are not only used to test the logic, but also to ensure that things work as expected when the implementation is changed. Let's say you write a test method which checks what's inside stream, later it's decided that you don't want to use streams, you'd have to change your test cases then to check if it does the same thing. – Rahul Sharma May 07 '16 at 18:02
  • @Rahul - I'm not sure I see your point. Obviously if an interface changes, the tests need to change too, streams or no streams. – Doradus May 07 '16 at 22:18

1 Answers1

6

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);
        }
    }
}
Yosef Weiner
  • 5,432
  • 1
  • 24
  • 37
  • 1
    Cool! So the core idea is that, since the mock listener doesn't actually consume the stream, you can write a `Matcher` that converts it to a list and compares that against `toMatch`. Is that close? – Doradus May 08 '16 at 01:03