我已经为Java 8编写了一个自定义收集器.它的聚合器是一个包含一对列表的Map:
@Override
public Supplier<Map<Boolean, List<Object>>> supplier() {
return () -> {
Map<Boolean, List<Object>> map = new HashMap<>(2);
map.put(false, new ArrayList<>());
map.put(true, new ArrayList<>());
return map;
};
}
Run Code Online (Sandbox Code Playgroud)
所以我认为它的组合是这样的:
@Override
public BinaryOperator<Map<Boolean, List<Object>>> combiner() {
return (a, b) -> {
a.get(false).addAll(b.get(false));
a.get(true).addAll(b.get(true));
return a;
};
}
Run Code Online (Sandbox Code Playgroud)
我想测试收集器以确保它是否以及何时并行处理流,结果是正确的.
我怎样才能编写一个单元测试呢?
当然我可以写一个combiner
直接调用的测试,但这不是我想要的.我希望证据在收集的背景下起作用.
Javadoc Collector
说:
为确保顺序和并行执行产生相同的结果,收集器函数必须满足标识和关联约束.
我可以通过测试这些约束来获得对收集器的信心吗?怎么样?
感谢两位回答者,他们让我走上了我认为正确的道路。
当然可以创建一个并行流来执行Collector
整体操作:
T result = myList.stream().parallel().collect(myCollector);
Run Code Online (Sandbox Code Playgroud)
但你不能保证它会分裂的边界,甚至不能保证它会分裂;缺少也许写一个自定义Spliterator
。
因此,测试合约似乎是一条可行的道路。相信Stream.collect()
只要可行,就会做正确的事Collector
。不测试“提供的”库是常见的做法。
JavaDocCollector
定义了约束,甚至提供了描述关联性约束的代码。我们可以将此代码放入可在现实世界中使用的测试类中:
public class CollectorTester<T, A, R> {
private final Supplier<A> supplier;
private final BiConsumer<A, T> accumulator;
private final Function<A, R> finisher;
private final BinaryOperator<A> combiner;
public CollectorTester(Collector<T, A, R> collector) {
this.supplier = collector.supplier();
this.accumulator = collector.accumulator();
this.combiner = collector.combiner();
this.finisher = collector.finisher();
}
// Tests that an accumulator resulting from the inputs supplied
// meets the identity constraint
public void testIdentity(T... ts) {
A a = supplier.get();
Arrays.stream(ts).filter(t -> t != null).forEach(
t -> accumulator.accept(a, t)
);
assertThat(combiner.apply(a, supplier.get()), equalTo(a));
}
// Tests that the combiner meets the associativity constraint
// for the two inputs supplied
// (This is verbatim from the Collector JavaDoc)
// This test might be too strict for UNORDERED collectors
public void testAssociativity(T t1, T t2) {
A a1 = supplier.get();
accumulator.accept(a1, t1);
accumulator.accept(a1, t2);
R r1 = finisher.apply(a1); // result without splitting
A a2 = supplier.get();
accumulator.accept(a2, t1);
A a3 = supplier.get();
accumulator.accept(a3, t2);
R r2 = finisher.apply(combiner.apply(a2, a3)); // result with splitting
assertThat(r1, equalTo(r2));
}
}
Run Code Online (Sandbox Code Playgroud)
剩下的就是用足够范围的输入来测试这一点。实现此目的的一种方法是使用 JUnit 4 的Theories
运行程序。例如,要测试Collectors.joining()
:
@RunWith(Theories.class)
public class MaxCollectorTest {
private final Collector<CharSequence, ?, String> coll = Collectors.joining();
private final CollectorTester<CharSequence, ?, String> tester = new CollectorTester<>(coll);
@DataPoints
public static String[] datapoints() {
return new String[] { null, "A", "rose", "by", "any", "other", "name" };
}
@Theory
public void testAssociativity(String t1, String t2) {
assumeThat(t1, notNullValue());
assumeThat(t2, notNullValue());
tester.testAssociativity(t1, t2);
}
@Theory
public void testIdentity(String t1, String t2, String t3) {
tester.testIdentity(t1, t2, t2);
}
}
Run Code Online (Sandbox Code Playgroud)
(我觉得很高兴的是,我的测试代码不需要知道 的Collectors.joining()
累加器的类型(API 未声明)即可使该测试正常工作)
请注意,这仅测试关联性和身份约束 - 您还需要测试收集器的域逻辑。collect()
通过检查 a 的结果和直接调用的方法的平衡组合来实现这一点可能是最安全的Collector
。
归档时间: |
|
查看次数: |
339 次 |
最近记录: |