我想要测试的交互之一是类Foo
应该将Stream<Changes>
传递给FooListener.someChangesHappened
。是否有Mockito习惯用法来验证流是否包含预期的对象?
假设您只是在验证mock实现的参数,而实际上并没有使用它,那么这里有一个自定义的Hamcrest Matcher
,它将完成任务。当您需要从Stream
读取多次时,它会变得很麻烦,因为Stream
并不是为此而构建的。您会注意到,这个解决方案甚至需要保护自己不受JUnit多次调用matches
的影响。
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);
}
}
}