You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Jqwik有状态测试时Action.Dependent链抛出JqwikException问题

解决Jqwik有状态测试中空动作链的问题

针对你遇到的动作链可能全选非生产性动作导致空链的问题,有几个直接有效的解决方案:

1. 强制动作链以生产性动作开头

最直接的方式是用ActionChain.startWith()指定必须先执行一个生产性动作,后续再随机选择其他动作(包括依赖状态的动作和生产性动作)。这样从根源上保证动作链至少包含一个有效动作,不会为空。

举个栈测试的代码示例:

// 定义生产性push动作(始终可执行)
Action<Stack<Integer>> pushAction = Action.start("push", stack -> {
    Arbitrary<Integer> values = Arbitraries.integers();
    return values.map(value -> s -> {
        s.push(value);
        return s;
    });
});

// 定义依赖状态的pop动作(栈非空时才可用)
Action<Stack<Integer>> popAction = Action.when("pop", stack -> !stack.isEmpty())
    .then(stack -> Arbitraries.just(s -> {
        s.pop();
        return s;
    }));

// 构建保证至少有一个生产性动作的动作链
@Provide
Arbitrary<ActionChain<Stack<Integer>>> validStackActionChains() {
    return ActionChain.startWith(pushAction)
        .addAction(pushAction, 2) // 可选:给push动作加权重,提高选中概率
        .addAction(popAction);
}

// 测试用例
@Property
void stackOperationsTest(@ForAll("validStackActionChains") ActionChain<Stack<Integer>> chain) {
    chain.run(Stack::new);
}

这种方式的核心是startWith()强制第一个动作是生产性的,后续动作可以正常随机选择,既保证了链非空,又保留了测试的随机性。

2. 自定义动作选择器确保至少选一个生产性动作

如果需要更灵活的控制(比如生产性动作不一定是第一个),可以实现自定义ActionSelector,在选择动作时跟踪是否已选过生产性动作:

  • 如果还没选过生产性动作,只从可用的生产性动作中选择
  • 一旦选过生产性动作,再正常混合选择所有可用动作

示例代码:

class AtLeastOneProductiveSelector implements ActionSelector {
    private final List<Action<?>> productiveActions;
    private final List<Action<?>> allActions;
    private boolean hasPickedProductive = false;

    public AtLeastOneProductiveSelector(List<Action<?>> productive, List<Action<?>> all) {
        this.productiveActions = productive;
        this.allActions = all;
    }

    @Override
    public <S> Arbitrary<Action<S>> select(ActionChain<S> chain) {
        List<Action<S>> applicableActions;
        if (!hasPickedProductive) {
            // 未选过生产性动作,仅从可用的生产性动作中选
            applicableActions = productiveActions.stream()
                .filter(action -> action.isApplicable(chain.currentState()))
                .map(action -> (Action<S>) action)
                .collect(Collectors.toList());
        } else {
            // 已选过,正常选所有可用动作
            applicableActions = allActions.stream()
                .filter(action -> action.isApplicable(chain.currentState()))
                .map(action -> (Action<S>) action)
                .collect(Collectors.toList());
        }
        return Arbitraries.of(applicableActions);
    }

    @Override
    public void reset() {
        hasPickedProductive = false;
    }

    @Override
    public void actionSelected(Action<?> action) {
        if (productiveActions.contains(action)) {
            hasPickedProductive = true;
        }
    }
}

使用自定义选择器构建动作链:

@Provide
Arbitrary<ActionChain<Stack<Integer>>> validStackActionChains() {
    List<Action<Stack<Integer>>> productive = List.of(pushAction);
    List<Action<Stack<Integer>>> allActions = List.of(pushAction, popAction);
    return ActionChain.<Stack<Integer>>create()
        .withActionSelector(new AtLeastOneProductiveSelector(productive, allActions))
        .addActions(allActions);
}

为什么independent()+endOfChain()没用?

ActionChain.independent()只是让动作选择不依赖之前的动作执行结果,但无法过滤掉全选非生产性动作的情况;endOfChain()仅用于控制链的结束条件,不能保证链中包含生产性动作,所以这两个组合解决不了你的核心问题。

内容的提问来源于stack exchange,提问作者Michał Kłeczek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 00:25:23