Command and environment

The static methods in CliAssured class, such as CliAssured.given() or CliAssured.command(String, String…​) are the main entry points into the CLI Assured API. They all return a CommandSpec instance.

Command and its arguments

0.0.1

The most common way to specify the executable and its arguments is to use CommandSpec.command(String executable, String…​ arguments).

The executable can be any of the following:

  • An absolute path

  • If CommandSpec.cd(path) was specified, then executable can be a path relative to that directory

  • A directory relative to the working directory of the current JVM

  • Plain command name of the command can be found in PATH environment variable.

import org.cliassured.CliAssured;
import org.cliassured.CommandSpec;

CliAssured
    // Call the echo command with two parameters "Hello" and "World!"
    .command("echo", "Hello", "World!")
    .execute()
    .assertSuccess();

Add arguments separately

0.0.1

If you need to add the arguments in several iterations, you can use the following pattern:

import org.cliassured.CliAssured;
import org.cliassured.CommandSpec;


// Set the executable echo
CommandSpec echo = CliAssured.given().executable("echo");

// Add arguments 1, 2 and 3
for (int i = 1; i < 4; i++) {
    echo = echo.arg(String.valueOf(i));
    // note that all CLI Assured API methods return an adjusted copy,
    // so we need to assign the result of echo.arg(...) back to echo
}

echo
    .then()
        .stdout()
            .log()
            // ensure 1 2 3 is printed
            .hasLines("1 2 3")
            // ... and nothing else
            .hasLineCount(1)
        .execute()
        .assertSuccess();

Special CommandSpec factories

CLI Assured offers special CommandSpec factories for calling the java command, for installing and invoking Maven, and for installing and invoking SDKMAN!.

Environment variables

0.0.1

Use CommandSpec.env(String name, String value, String…​ more) to set one or more environment variables for the command. Note that the number of additional arguments must be even. Each name must have a corresponding value. Here is a basic example:

import org.cliassured.CliAssured;

CliAssured
    .given()
        // Set multiple environment variables as name-value pairs
        .env("FIRST", "Hello",
             "SECOND", "World")
    .when()
        // Use the environment variables that were set above
        .command("sh", "-c", "echo $FIRST $SECOND")
    .then()
        .stdout()
            // Make sure the values of the variables are present in stdout
            .hasLines("Hello World")
    .execute()
    .assertSuccess();

There is also CommandSpec.env(Map<String, String> variables) accepting a map from variable names to their respective values.

Working directory

0.0.1

Use CommandSpec.cd(Path workDirectory) to set the working directory of the spawned process. The command’s executable and any relative file paths will be resolved against this directory. If cd() is not called, the working directory defaults to the current JVM’s working directory.

import java.nio.file.Files;
import java.nio.file.Path;
import org.cliassured.CliAssured;

// Create a file in a temporary directory
Path myWorkDir = Files.createDirectory(tempDir.resolve("my-workdir"));
Files.writeString(myWorkDir.resolve("hello.txt"), "Hello from my-workdir!");

CliAssured
    .given()
        // Set the working directory for the command
        .cd(myWorkDir)
    .when()
        // cat resolves the file relative to the working directory we have set above
        .command("cat", "hello.txt")
    .then()
        .stdout()
            // Ensure that the content of hello.txt is printed to stdout
            .hasLines("Hello from my-workdir!")
    .execute()
    .assertSuccess();

given() and when() methods we used in the above snippet are just syntactic sugar. Using CliAssured.command("cat", "hello.txt").cd(myWorkDir) would have exactly the same effect.

Redirect stderr to stdout

0.0.1

Use CommandSpec.stderrToStdout() to redirect the standard error output of the spawned process to its standard output. This is useful when you want to assert on stderr content using the stdout() assertions, or when the command mixes output across both streams and you want to capture everything in one place.

Note that once stderrToStdout() is enabled, calling stderr() on the expectations will throw an IllegalStateException.

import org.cliassured.CliAssured;

CliAssured
    // Run a command that writes to stderr
    .command("sh", "-c", "echo 'error message' >&2")
    // Redirect stderr to stdout so it can be asserted via stdout()
    .stderrToStdout()
    .then()
        .stdout()
            // The stderr output is now available through stdout
            .hasLines("error message")
    .execute()
    .assertSuccess();

Write to stdin

0.0.1

Write a string to stdin

0.0.1

Use CommandSpec.stdin(String) to pass a string to the process' standard input using UTF-8 encoding. This is a convenience shorthand for the IoConsumer<OutputStream> variant when you have all input data upfront. You may call only one of the stdin(…​) methods per CommandSpec chain.

import org.cliassured.CliAssured;

CliAssured
    // cat with no arguments reads from stdin
    .command("cat")
    // Pass a string to the process' stdin using UTF-8 encoding
    .stdin("Hello from stdin!")
    .then()
        .stdout()
            // The string appears in stdout
            .hasLines("Hello from stdin!")
    .execute()
    .assertSuccess();

Send a file to stdin

0.0.1

Use CommandSpec.stdin(Path) to pass the contents of a file to the process' standard input. You may call only one of the stdin(…​) methods per CommandSpec chain.

import java.nio.file.Files;
import java.nio.file.Path;
import org.cliassured.CliAssured;

// Create a file with some content
Path inputFile = tempDir.resolve("input.txt");
Files.writeString(inputFile, "Hello from file!");

CliAssured
    // cat with no arguments reads from stdin
    .command("cat")
    // Pass the file contents to the process' stdin
    .stdin(inputFile)
    .then()
        .stdout()
            // The file contents appear in stdout
            .hasLines("Hello from file!")
    .execute()
    .assertSuccess();

Write bytes to stdin

0.0.1

Use CommandSpec.stdin(Consumer<OutputStream>) to write arbitrary data to the process' standard input. The consumer receives an OutputStream connected to the process' stdin and is called on a dedicated thread. CLI Assured closes the stream automatically after the consumer returns.

Exceptions thrown from the consumer are caught and reported when CommandResult.assertSuccess() is called. You may call only one of the stdin(…​) methods per CommandSpec chain.

import java.nio.charset.StandardCharsets;
import org.cliassured.CliAssured;

CliAssured
    // cat with no arguments reads from stdin
    .command("cat")
    // Write bytes to the process' stdin
    .stdin(out -> out.write("Hello from stdin!".getBytes(StandardCharsets.UTF_8)))
    .then()
        .stdout()
            // The data written to stdin appears in stdout
            .hasLines("Hello from stdin!")
    .execute()
    .assertSuccess();

Auto-close behavior

0.0.1

CommandProcess implements AutoCloseable, so you can use it in a try-with-resources block. When the block exits, close() kills the underlying process and waits for its termination.

By default, close() sends a graceful termination signal (Process.destroy(), typically SIGTERM on Linux) and waits indefinitely. You can change this behavior with the following methods:

  • autoCloseForcibly() — use Process.destroyForcibly() (SIGKILL) instead of Process.destroy() (SIGTERM)

  • autoCloseTimeout(Duration) — limit how long close() waits for the process to terminate

  • autoCloseWithoutDescendants() — kill only the main process, not its descendant processes (on Java 9+, descendants are killed by default)

import java.time.Duration;
import org.cliassured.CliAssured;
import org.cliassured.CommandProcess;

// Start a long-running process using try-with-resources
try (CommandProcess proc = CliAssured
        .command("sleep", "60")
        // Use forcible kill (SIGKILL) when closing instead of the default graceful kill (SIGTERM)
        .autoCloseForcibly()
        // Wait up to 5 seconds for the process to terminate after closing
        .autoCloseTimeout(Duration.ofSeconds(5))
        .then()
            // SIGKILL=137, SIGTERM=143
            .exitCodeIsAnyOf(137, 143)
        .start()) {

    // Do some work while the process is running...
    System.out.println("`sleep 60` has PID" + proc.pid());

} // The process is forcibly killed here when the try block exits

Thread pool

0.0.1

By default, CLI Assured uses a single global thread pool (see CliAssured.globalThreadPool()) for consuming stdout, stderr and producing stdin of all commands. Use CommandSpec.threadPool() to assign a dedicated per-command thread pool with custom coreSize, maxSize and keepAlive settings:

import java.time.Duration;
import org.cliassured.CliAssured;

CliAssured
    .command("echo", "Hello!")
    // Use a per-command thread pool
    .threadPool()
        // 2 core threads
        .coreSize(2)
        // Up to 4 threads
        .maxSize(4)
        // Idle threads removed after 30s
        .keepAlive(Duration.ofSeconds(30))
    .execute()
    .assertSuccess();

Supply a custom ExecutorService

0.0.1

If you need full control, use CommandSpec.threadPool(Supplier<ExecutorService>) to provide your own ExecutorService factory. A new ExecutorService is created from the supplier each time the command is started.

import java.util.concurrent.Executors;
import org.cliassured.CliAssured;

CliAssured
    .command("echo", "Hello!")
    // Supply a custom ExecutorService
    .threadPool(Executors::newCachedThreadPool)
    .execute()
    .assertSuccess();

Next steps

Once you have configured a CommandSpec instance, you may want to do one of the following: