picocli
picocli copied to clipboard
Create simple test framework for testing JVM and native CLI apps
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.
@maxandersen tweeted about these interesting alternatives:
- https://github.com/zeroturnaround/zt-exec
- http://www.awaitility.org/
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..
https://github.com/stefanbirkner/system-lambda might be an option as well
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"));
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 );
}
}