Testing
Spring Shell provides several utilities to facilitate testing of shell applications. These utilities help simulate user input, capture output, and verify command behavior in a controlled environment.
Test assertions
Spring Shell offers the following test assertion APIs to validate command execution and output:
-
ShellScreen: This class represents the shell screen and allows you to capture and analyze the output displayed to the user. -
ShellAssertions: This class provides static methods to assert the results of shell command executions. -
ShellTestClient: This class allows you to simulate user input and execute shell commands programmatically.
Here is an example of how to use these APIs in a test:
@ExtendWith(SpringExtension.class)
class ShellTestClientTests {
@Test
void testCommandExecution(@Autowired ShellTestClient shellTestClient) throws Exception {
// when
ShellScreen shellScreen = shellTestClient.sendCommand("test");
// then
ShellAssertions.assertThat(shellScreen).containsText("Test command executed");
}
}
Test annotations
Spring Shell provides the @ShellTest annotation which is used to indicate that a test class is a Spring Shell test. It sets up the necessary context for testing shell commands. This annotation is defined in the spring-shell-test-autoconfigure module, and is designed to be used with Spring Boot applications.
Once you define your Spring Boot application class, you can create a test class annotated with @ShellTest to test your shell commands. Here is an example:
@SpringBootApplication
public class ExampleShellApplication {
@Command(name = "hi", description = "Says hello")
public String hello() {
return "hello";
}
}
@ShellTest
@ContextConfiguration(classes = ExampleShellApplication.class)
class ShellTestIntegrationTests {
@Test
void testCommandExecution(@Autowired ShellTestClient client) throws Exception {
// when
ShellScreen shellScreen = client.sendCommand("hi");
// then
ShellAssertions.assertThat(shellScreen).containsText("hello");
}
@Test
void testUnknownCommandExecution(@Autowired ShellTestClient client) {
Assertions.assertThatThrownBy(() -> client.sendCommand("foo"))
.isInstanceOf(CommandNotFoundException.class);
}
}
Spring Shell also provides the ShellInputProvider API which can be used to provide custom input for testing purposes. This allows you to simulate user input (clear texts and passwords) in a more flexible way.
For example, if your command expects input as follows:
@Bean
public Command ask() {
return new AbstractCommand("ask", "Ask for user input") {
@Override
public ExitStatus doExecute(CommandContext commandContext) throws Exception {
String message = commandContext.inputReader().readInput();
commandContext.outputWriter().println("You said: " + message);
return ExitStatus.OK;
}
};
}
then you can simulate the user’s input by using ShellInputProvider to supply the necessary input during testing:
@Test
void testCommandExecutionWithReadingInput(@Autowired ShellTestClient client) throws Exception {
// given
ShellInputProvider inputProvider = ShellInputProvider.providerFor("ask").withInput("hi").build();
// when
ShellScreen screen = client.sendCommand(inputProvider);
// then
ShellAssertions.assertThat(screen).containsText("You said: hi");
}
End-to-End Testing
For end-to-end (ie black-box) testing of shell applications without using Spring Shell testing facilities, you can use the test utilities provided by Spring Boot. Once you have defined your Spring Boot application class, you can create a test class annotated with @SpringBootTest to test your shell commands. Here is an example:
@SpringBootApplication
public class MyShellApplication {
public static void main(String[] args) {
SpringApplication.run(MyShellApplication.class, args);
}
@Command
public void hi() {
System.out.println("Hello world!");
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
useMainMethod = SpringBootTest.UseMainMethod.ALWAYS,
classes = { MyShellApplication.class },
properties = { "spring.shell.interactive.enabled=false" },
args = "hi")
@ExtendWith(OutputCaptureExtension.class)
public class ShellApplicationEndToEndTests {
@Test
void testCommandOutput(CapturedOutput output) {
assertThat(output).contains("Hello world!");
}
}
This example demonstrates how to run a Spring Shell application in a test context and verify the output of a command using Spring Boot’s OutputCaptureExtension.
The caveat with this approach is that it is not convenient for testing multiple commands in the same test class due to the fact that the command is passed through the args property of the @SpringBootTest annotation. For testing multiple commands, it is recommended to use the Spring Shell testing utilities as described in the previous sections. Here is an example of how to test multiple commands using the Spring Shell testing utilities:
@SpringBootApplication
public class GreetingShellApplication {
public static void main(String[] args) {
SpringApplication.run(GreetingShellApplication.class, args);
}
@Command
public void hi(CommandContext context) {
context.outputWriter().println("Hello world!");
}
@Command
public void bye(CommandContext context) {
context.outputWriter().println("Goodbye world!");
}
}
@ShellTest
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
useMainMethod = SpringBootTest.UseMainMethod.ALWAYS, classes = { GreetingShellApplication.class },
properties = { "spring.shell.interactive.enabled=false" })
public class ShellApplicationEndToEndMultipleCommandsTests {
@Test
void testCommandExecution(@Autowired ShellTestClient client) throws Exception {
// when
ShellScreen shellScreen = client.sendCommand("help");
// then
ShellAssertions.assertThat(shellScreen).containsText("AVAILABLE COMMANDS");
}
@Test
void testHiCommandExecution(@Autowired ShellTestClient client) throws Exception {
// when
ShellScreen shellScreen = client.sendCommand("hi");
// then
ShellAssertions.assertThat(shellScreen).containsText("Hello world!");
}
@Test
void testByeCommandExecution(@Autowired ShellTestClient client) throws Exception {
// when
ShellScreen shellScreen = client.sendCommand("bye");
// then
ShellAssertions.assertThat(shellScreen).containsText("Goodbye world!");
}
}