picocli icon indicating copy to clipboard operation
picocli copied to clipboard

Create simple test framework for testing JVM and native CLI apps

Open remkop opened this issue 5 years ago • 6 comments

Create simple test framework for testing JVM and native CLI apps.

Ideally allows the same test to be applied to a CommandLine object executed in the JVM, as well as to a native image executed as a separate Process.

remkop avatar Dec 04 '19 16:12 remkop

@maxandersen tweeted about these interesting alternatives:

  • https://github.com/zeroturnaround/zt-exec
  • http://www.awaitility.org/

remkop avatar Jun 26 '20 03:06 remkop

there is https://github.com/intuit/karate/issues/1191 too - but i think some small java utility to just test cli's and assert on stdout/err as strings and potentially streams too would still be powerful..

maxandersen avatar Jun 26 '20 07:06 maxandersen

https://github.com/stefanbirkner/system-lambda might be an option as well

sebhoss avatar Sep 02 '20 07:09 sebhoss

I am using system-lambda in this PR (not yet merged) : https://github.com/project-ncl/bacon/pull/431/files#diff-006308d763b4c36013a8c98e99b01992R63 so you can do

        String text = tapSystemErr(
                () -> assertEquals(
                        1,
                        new App().run(
                                new String[] { "-o", "-p", configYaml.toString(), "-v", "pnc", "build", "get", "0" })));
        assertTrue(text.contains("JSON command is enabled: true"));

rnc avatar Sep 08 '20 18:09 rnc

Hi @remkop,

I don't know if you already have an idea of how this would be modeled. If not, I have a suggestion:

What we have done to test our CLI application in JVM and Native modes is to create a set of classes:

  • An Abstract class to test the application that dynamically selects the testing mode depending on which tests we use it in
  • Classes that know how to run the app depending on the test mode
  • A class with all the command line options of the application

Here is a pseudo code example:

If you have a command class:

@Command()
public class MyCommand {
	@Option
    private Integer one;

    @Option
    private String two;
}

PicoCLI would generate this classes:

public abstract class MyCommandLauncher {
	public static MyCommandLauncher create() {
		return native ? new MyCommandLauncherNative() : new MyCommandLauncherJVM();
	}

	public abstract List<String> getCommand();

	public int run( MyCommandParameters parameters ) {
		Process process = new ProcessBuilder( getCommand().addAll( parameters.getParameters() ).start();
		
		process.waitFor();
        
        return process.exitValue();
	}
}
protected class MyCommandLauncherJVM extends MyCommandLauncher {
	public List<String> getCommand() {
		// return the command line to launch the jar file.
	}
}
protected class MyCommandLauncherNative extends MyCommandLauncher {
	public List<String> getCommand() {
		// return the command line to launch the native executable.
	}
}
// This class could use a constructor or a builder pattern
public class MyCommandParameters {
    private Integer one;
    private String two;

	//  Getters and Setters

	public List<String> getParameters() {
		// return the parameters for the cli
	}
}

And they could be used in a test:

class MyCommandTest {
	MyCommandLauncher myApp = MyCommandLauncher.create();

	@Test 
	void myTest() {
		MyCommandParameters params = MyCommandParameters.builder().one( 1 ).two ( "two" ).build();

		int exitValue = myApp.run( params );

		assertThat( exitValue ).isEqualTo( 0 );
	}
}

ebramirez avatar Oct 05 '20 08:10 ebramirez

@ebramirez I did some initial prototyping here. Example usage here. I haven't spent much time on this though.

remkop avatar Oct 06 '20 12:10 remkop