Как написать метод генератора jqwik с вложенными генераторами - PullRequest
1 голос
/ 23 октября 2019

Используя jqwik.net, пытаемся сгенерировать класс Rule с вложенным классом RuleConfig внутри него. Класс RuleConfig имеет вложенный ruleProps, который является Map

. Метод statusReturnedFromApplyingRule всегда возвращает инициализированное правило вместо использования значений метода @provide ?? Правило возврата: rule:Rule{ruleId='null', inputMetricSelector=null, ruleConfig='RuleConfig{ruleType='null', ruleProps={}}'}, elements:[{}]

Вот мой код:

public class RangeMatchRuleTest {

    @Property
    @Report(Reporting.GENERATED)
    boolean statusReturnedFromApplyingRule(@ForAll("generateRule") Rule rule,
                                           @ForAll("generateInputMapElements") Iterable<Map<String, Object>> elements) {
        RangeMatchRule rangeMatchRule = new RangeMatchRule();
        final RuleIF.Status status = rangeMatchRule.applyRule(rule, elements);
        return RuleIF.getEnums().contains(status.toString());
    }

    @Provide
    Arbitrary<Rule> generateRule() {
        Rule rule = new Rule();
        RuleConfig ruleConfig = new RuleConfig();
        Map<String, Object> ruleProps = new HashMap<>();

        Arbitrary<Double> lowThresholdArb = Arbitraries.doubles()
                .between(0.0, 29.0);
        lowThresholdArb.allValues().ifPresent(doubleStream -> ruleProps.put(Utils.LOW_THRESHOLD, doubleStream.findFirst().get()));
        //lowThresholdArb.map(lowThreshold -> ruleProps.put(Utils.LOW_THRESHOLD, lowThreshold) );
        Arbitrary<Double> highThresholdArb = Arbitraries.doubles()
                .between(30.0, 50.0);
        highThresholdArb.map(highThreshold -> ruleProps.put(Utils.HIGH_THRESHOLD, highThreshold));
        ruleConfig.setRuleProps(ruleProps);
        rule.setRuleConfig(ruleConfig);
        return Arbitraries.create(() -> rule);
    }

    @Provide
    Arbitrary<Iterable<Map<String, Object>>> generateInputMapElements() {
        Arbitrary<Double> metricValueArb = Arbitraries.doubles()
                .between(0, 50.0);
        Map<String, Object> inputMap = new HashMap<>();
        metricValueArb.map(metricValue -> inputMap.put(Utils.METRIC_VALUE, metricValue));
        List<Map<String, Object>> inputMapLst = new ArrayList<>();
        inputMapLst.add(inputMap);
        return Arbitraries.create(() -> inputMapLst);
    }
}

TIA

Ответы [ 2 ]

1 голос
/ 23 октября 2019

Вы строите метод generateRule из-за неверного предположения, что произвольный метод map выполнил любое реальное действие при вызове. Это не вариант. Тот факт, что map возвращает другой произвольный экземпляр, дает сильный намек.

Основная идея, которую вы должны понять, заключается в том, что метод провайдера - метод, аннотированный @Provide, - это не что иное, как «описание»процесс генерации;он будет вызван только один раз. Фактическая генерация объекта происходит впоследствии и контролируется платформой.

Вот переработанный generateRule метод, который должен делать то, что вы хотели:

@Provide
Arbitrary<Rule> generateRule() {
    Arbitrary<Double> lowThresholdArb = Arbitraries.doubles()
                                                   .between(0.0, 29.0);
    Arbitrary<Double> highThresholdArb = Arbitraries.doubles()
                                                    .between(30.0, 50.0);

    Arbitrary<RuleConfig> configArb =
        Combinators.combine(lowThresholdArb, highThresholdArb)
                   .as((low, high) -> {
                       Map<String, Object> ruleProps = new HashMap<>();
                       ruleProps.put(Utils.LOW_THRESHOLD, low);
                       ruleProps.put(Utils.HIGH_THRESHOLD, high);
                       RuleConfig ruleConfig = new RuleConfig();
                       ruleConfig.setRuleProps(ruleProps);
                       return ruleConfig;
                   });

    return configArb.map(config -> {
        Rule rule = new Rule();
        rule.setRuleConfig(config);
        return rule;
    });
}

То, что вы можете надеяться увидеть, это то, что созданиегенератор подобен программированию потока данных: начиная с некоторых базовых произвольных библиотек - lowThresholdArb и highThresholdArb - вы объединяете, отображаете и фильтруете их. В конце должен быть возвращен единственный экземпляр Arbitrary.

Кстати: если вы хотите, чтобы этот генератор применялся каждый раз, когда вам нужен Rule, вы можете написать следующий класс:

public class RuleArbitraryProvider implements ArbitraryProvider {

    @Override
    public boolean canProvideFor(TypeUsage targetType) {
        return targetType.isOfType(Rule.class);
    }

    @Override
    public Set<Arbitrary<?>> provideFor(TypeUsage targetType, SubtypeProvider subtypeProvider) {
        return Collections.singleton(generateRule());
    }

    private Arbitrary<Rule> generateRule() {
        // Put here the code from above
        ...
    }
}

и регистрируют его в качестве поставщика по умолчанию .

0 голосов
/ 23 октября 2019

Дополнительный пример для карты выше на основе предоставленного ответа:

    @Provide
Arbitrary<Iterable<Map<String, Object>>> generateInputMapElements() {
    Arbitrary<Double> metricValueArb = Arbitraries.doubles()
            .between(0, 50.0);

    Arbitrary<Map<String, Object>> inputMapArb =
            metricValueArb.map(metricsValue -> {
                Map<String, Object> inputMap = new HashMap<>();
                inputMap.put(Utils.METRIC_VALUE, metricsValue);
                return inputMap;
            });
    return inputMapArb.map(inputMap -> {
        List<Map<String, Object>> inputMapLst = new ArrayList<>();
        inputMapLst.add(inputMap);
        return inputMapLst;
    });
}
...