Testing Commands
Test Crest commands in-process using Main.builder() and PrintString.
Testing with Main.builder()
Crest commands are plain Java methods, but testing through the full CLI pipeline ensures that argument parsing, defaults, validation, and output all work correctly. The Main.builder() API makes this straightforward by letting you run commands in-process without ServiceLoader discovery or System.exit calls.
Capturing Output with PrintString
Use PrintString from tomitribe-util to capture command output. Pass it to the builder via out(), then assert against its contents after running the command:
import org.junit.Test;
import org.tomitribe.crest.Main;
import org.tomitribe.util.PrintString;
import static org.junit.Assert.assertEquals;
public class HelloCommandTest {
@Test
public void testDefaultGreeting() throws Exception {
final PrintString out = new PrintString();
final Main main = Main.builder()
.command(HelloCommand.class)
.out(out)
.build();
main.run("hello");
assertEquals(String.format("Hello, World!%n"), out.toString());
}
@Test
public void testCustomName() throws Exception {
final PrintString out = new PrintString();
final Main main = Main.builder()
.command(HelloCommand.class)
.out(out)
.build();
main.run("hello", "--name", "Alice");
assertEquals(String.format("Hello, Alice!%n"), out.toString());
}
}
Asserting Full Output
Always assert the complete output with assertEquals rather than partial matches with contains. This catches unexpected extra output, missing newlines, and formatting regressions.
Use String.format() with %n for platform-independent newline characters. This ensures tests pass on both Unix and Windows:
@Test
public void testHelpListing() throws Exception {
final PrintString out = new PrintString();
final Main main = Main.builder()
.command(HelloCommand.class)
.out(out)
.build();
main.run("help");
assertEquals(String.format("Commands: %n" +
"%n" +
" hello Greet someone by name%n" +
" help %n"), out.toString());
}
Capturing Error Output
Use a separate PrintString for stderr when testing error scenarios:
@Test
public void testErrorOutput() throws Exception {
final PrintString out = new PrintString();
final PrintString err = new PrintString();
final Main main = Main.builder()
.command(HelloCommand.class)
.out(out)
.err(err)
.build();
main.run("hello", "--unknown-flag");
// Assert against err.toString() for error messages
}
Getting Return Values with exec()
When a command returns a value and you want to inspect it directly rather than through printed output, use exec():
@Test
public void testReturnValue() throws Exception {
final Main main = Main.builder()
.command(HelloCommand.class)
.build();
final Object result = main.exec("hello", "--name", "Alice");
assertEquals("Hello, Alice!", result);
}
Key Testing Patterns
- Use
Main.builder()to construct an isolatedMaininstance with only the commands under test. - Use
PrintStringto capture stdout and stderr as strings for assertion. - Use
String.format("%n")for newlines in expected output so tests are platform-independent. - Assert full output with
assertEquals, not partial matches withcontains. - Use
run()when testing void commands or side effects. Useexec()when you need the return value.