【问题标题】:How to write a jqwik generator method with nested generators如何使用嵌套生成器编写 jqwik 生成器方法
【发布时间】:2019-10-22 21:55:53
【问题描述】:

使用 jqwik.net,尝试生成一个 Rule 类,其中包含一个嵌套的 RuleConfig 类。 RuleConfig 类有一个嵌套的 ruleProps,它是一个 Map

statusReturnedFromApplyingRule 方法总是返回一个初始化的 Rule 而不是使用 @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

【问题讨论】:

    标签: java testing junit5 jqwik


    【解决方案1】:

    您正在构建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;
        });
    }
    

    您希望看到的是,创建生成器就像数据流编程:从一些基本的任意值开始 - lowThresholdArbhighThresholdArb - 你将它们组合、映射和过滤。最后必须返回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
            ...
        }
    }
    

    register it as a default provider

    【讨论】:

      【解决方案2】:

      基于提供的答案的上述地图的其他示例:

          @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;
          });
      }
      

      【讨论】:

      • 如何循环使用上述代码生成输入映射元素以使用动态任意整数值生成 Map 列表?如上所述,每个地图都有 1 个 METRIC_VALUE 条目。基本上,我如何将上面的代码包装在一个可变数量的整数值的循环中? @johanneslink
      • 不需要循环。为地图使用任意创建者:jqwik.net/docs/current/user-guide.html#maps
      • 如果您提出新问题,我可以尝试解释解决方案。
      • @johanneslink 添加了一个新主题stackoverflow.com/questions/58548769/…
      猜你喜欢
      • 1970-01-01
      • 2011-09-19
      • 1970-01-01
      • 2017-08-16
      • 1970-01-01
      • 2020-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多