picocli icon indicating copy to clipboard operation
picocli copied to clipboard

Execution result is null when calling subcommands with CommandLine

Open PakhomovAlexander opened this issue 2 years ago • 3 comments

Hello, I ran into an issue during testing subcommands. I am trying to assert the output of the subcommand with CommandLine#getExcecutionResult and it is null.

@Command(subcommands = CallableSubCommand.class)
    public static class CallableCommand implements Callable<String> { // top level command
        @Override
        public String call() {
            return "Result";
        }
    }

    @Command(name = "sub")
    public static class CallableSubCommand implements Callable<String> { // subcommand that I'd like to test
        @Override
        public String call() {
            System.out.println("I am executed!"); // this will be printed in the console
            return "Result subcommand"; // but this result is unreachable after subcommand is executed
        }
    }

    public static void main(String[] args) {
        CommandLine commandLine = new CommandLine(new CallableCommand()); 
        String[] argv = {"sub"};
        commandLine.execute(argv);
        System.out.println(commandLine.getExecutionResult().toString()); // NPE: commandLine.getExecutionResult() is null
    }

I can test CallableSubCommand directly as a separate command and it is working but I would like to test it in the context of the top-level command.

Also, I can set my own string writer as a system and use Runnable instead of Callable. But the behavior I described seems to be a bug. Thank you.

Picocli version: 4.6.2

PakhomovAlexander avatar Apr 15 '22 10:04 PakhomovAlexander

I think this is not the issue of testing, but the issue of Callable subcommands.

PakhomovAlexander avatar Apr 15 '22 10:04 PakhomovAlexander

You are invoking the subcommand, but are testing the execution result of the top-level command. Have you tried getting the execution result for the subcommand?

commandLine.getSubcommands().get("sub").getExecutionResult()

remkop avatar Apr 16 '22 01:04 remkop

@PakhomovAlexander Did that resolve the issue?

remkop avatar Apr 20 '22 00:04 remkop

@remkop I just stumbled upon the very same question and found your answer, thanks! Is there a way to get the execution result of the last executed subcommand without having to figure out by oneself?

rdesgroppes avatar May 24 '23 17:05 rdesgroppes

Yes:

The ParseResult can be used to get the return value from a Callable or Method subcommand:

// getting return value from Callable or Method command
Object topResult = cmd.getExecutionResult();

// getting return value from Callable or Method subcommand
ParseResult parseResult = cmd.getParseResult();
if (parseResult.subcommand() != null) {
    CommandLine sub = parseResult.subcommand().commandSpec().commandLine();
    Object subResult = sub.getExecutionResult();
}

rdesgroppes avatar May 24 '23 20:05 rdesgroppes

@rdesgroppes You may be interested in the ParseResult.asCommandLineList method, which returns the hierarchy of commands that the end user invoked.

The last command in the list is the most specific subcommand that the end user specified on the command line.

(Depending on your use case, you may need to do some special processing if you have repeatable subcommands enabled.)

remkop avatar May 25 '23 06:05 remkop