Произвольный, созданный с помощью flatMap, не учитывает фильтр
Я пытаюсь использовать jqwik (версия 1.5.1), и я прочитал из документации, что могу создать сгенерированное значение, значение которого зависит от значения, предоставленного другим, в частности, используя
flatMap
функция.
Моя настоящая цель другая, но основанная на этой идее: мне нужно 2
Arbitrary
s, которые всегда генерируют разные значения для одного теста. Вот что я пробовал:
@Provide
private Arbitrary<Tuple.Tuple2<Integer, Integer>> getValues() {
var firstArbitrary = Arbitraries.integers().between(1, Integer.MAX_VALUE);
var secondArbitrary = firstArbitrary.flatMap(first ->
Arbitraries.integers().between(1, Integer.MAX_VALUE).filter(i -> !i.equals(first)));
return Combinators.combine(firstArbitrary, secondArbitrary).as(Tuple::of);
}
@Property
public void test(@ForAll("getValues") Tuple.Tuple2<Integer, Integer> values) {
assertThat(values.get1()).isNotEqualTo(values.get2());
}
И с этим образцом сразу же выходит из строя:
Shrunk Sample (1 steps)
-----------------------
arg0: (1, 1)
Бросив
AssertionError
конечно:
java.lang.AssertionError:
Expecting:
1
not to be equal to:
1
Я ожидал
filter
функции было бы достаточно, чтобы исключить сгенерированное значение, произведенное
firstArbitrary
но кажется, что это даже не рассматривается, или, скорее, это что-то другое. Что мне не хватает? Есть ли более простой способ убедиться в этом, учитывая определенное количество
integer
генераторы, они всегда производят разные значения?
1 ответ
Общая идея одной созданной ценности, влияющей на следующее поколение шага через
flatMap
правильно. Вам не хватает того, что вы теряете эту связь, комбинируя
firstArbitrary
а также
secondArbitrary
вне области плоского отображения. Исправление незначительное:
@Provide
private Arbitrary<Tuple.Tuple2<Integer, Integer>> getValues() {
var firstArbitrary = Arbitraries.integers().between(1, Integer.MAX_VALUE);
return firstArbitrary.flatMap(
first -> Arbitraries.integers().between(1, Integer.MAX_VALUE)
.filter(i -> !i.equals(first))
.map(second -> Tuple.of(first, second))
);
}
Тем не менее, есть и другие - я бы сказал проще - способов достижения вашей цели:
@Provide
private Arbitrary<Tuple.Tuple2<Integer, Integer>> getValues() {
var firstArbitrary = Arbitraries.integers().between(1, Integer.MAX_VALUE);
return firstArbitrary.tuple2().filter(t -> !t.get1().equals(t.get2()));
}
Это избавляет от плоского сопоставления, что означает меньше усилий при сжатии для jqwik.
Другое возможное решение:
@Provide
private Arbitrary<Tuple.Tuple2<Integer, Integer>> getValues() {
var firstArbitrary = Arbitraries.integers().between(1, Integer.MAX_VALUE);
return firstArbitrary.list().ofSize(2).uniqueElements().map(l -> Tuple.of(l.get(0), l.get(1)));
}
Это может показаться немного сложным, но у него есть то преимущество, что не используется плоское сопоставление и фильтрация. Фильтрация часто снижает производительность генерации, крайние случаи, исчерпывающую генерацию и сжатие. Вот почему я избегаю фильтрации, когда могу, без особых хлопот.