Testing commands¶
Warning
Commands execution overrides System IO and so can't run in parallel with other tests!
Use @Isolated
on such tests to prevent parallel execution with other tests
Command execution is usually a short-lived action, so it is not possible to write an extension for it. Command could be tested only with generic utility:
@Test
public class testCommand() {
CommandResult result = TestSupport.buildCommandRunner(App.class)
.run("cmd", "-p", "param");
Assertions.assertTrue(result.isSuccessful());
}
This runner could be used to run any command type (simple, configured, environment).
The type of command would define what objects would be present ofter the command execution
(for example, Injector
would be available only for EnvironmentCommand
).
Run command arguments are the same as real command arguments (the same Cli
used for commands parsing).
You can only omit configuration path and use builder instead:
CommandResult result = TestSupport.buildCommandRunner(App.class)
.config("path/to/config.yml")
.configOverride("prop: 1")
.run("cmd", "-p", "param");
Important
Command execution never throws an exception - any appeared exception would be inside resulted object:
Assertions.assertFalse(result.isSuccessful());
Assertions.assertEquals("Error message", result.getException().getMessage());
Output is intercepted and could be used for assertions:
Assertions.assertTrue(result.getOutput().contains("some text"))
All special objects (like configuration, environment etc), created during command execution are all stored inside the result object (this is the only way to access them).
IO¶
Runner use System.in/err/out replacement. All output is intercepted and could be asserted:
Assertions.assertTrue(result.getOutput().contains("some text"))
result.getOutput()
contains both out
and err
streams together
(the same way as user would see it in console). Error output is also available
separately with result.getErrorOutput()
.
Note
All output is always printed to console, so you could always see it after test execution (without additional actions)
Commands requiring user input could also be tested (with mocked input):
CommandResult result = TestSupport.buildCommandRunner(App.class)
.consoleInputs("1", "two", "something else")
.run("quiz")
At least, the required number of answers must be provided (otherwise error would be thrown, indicating not enough inputs)
Warning
Due to IO overrides, command tests could not run in parallel.
For junit 5, such tests could be annotated with @Isolated
(to prevent execution in parallel with other tests)
Configuration¶
Configuration options are the same as in run builder. For example:
// override only
TestSupport.buildCommandRunner(App.class)
.configOverride("foo: 12")
.run("cfg");
// file with overrides
TestSupport.buildCommandRunner(App.class)
.config("src/test/resources/path/to/config.yml")
.configOverride("foo: 12")
.run("cfg");
// direct config object
MyConfig config = new MyConfig();
TestSupport.buildCommandRunner(App.class)
.config(config)
.run("cfg");
Note
Config file should not be specified in command itself - builder would add it, if required.
But still, it would not be a mistake to use config file directly in command:
TestSupport.buildCommandRunner(App.class)
// note .config("...") was not used (otherwise two files would appear)!
.run("cfg", "path/to/config.yml");
Using builder for config file configuration assumed to be a preferred way.
Listener¶
There is a simple listener support (like in application run builder) for setup-cleanup actions:
TestSupport.buildCommandRunner(App.class)
.listen(new CommandRunBuilder.CommandListener<>() {
public void setup(String[] args) { ... }
public void cleanup(CommandResult<TestConfiguration> result) { ... }
})
.run("cmd")