skip to content

What does the `args = {}` attribute of `@SpringBootTest` do, and how do those values reach the application?

level: middleimportance: should knowfreq 40%

answer

  1. String[] into SpringApplication.run
  2. --key=value → commandLineArgs property source
  3. non-option args only via ApplicationArguments
  4. drives CommandLineRunner/ApplicationRunner
  5. lower precedence than properties={}

basics

~10 s

args is the String[] passed to SpringApplication.run(args), exactly like real command-line arguments. Option args like --app.mode=fast become properties; you can also inject the whole ApplicationArguments bean to read them.

solid answer

~40 s

`@SpringBootTest(args = {"--app.mode=fast", "import", "file.csv"})` simulates launching the app from the command line: the array is handed to `SpringApplication.run(args)`. Two things happen. First, **option arguments** (`--key=value`) are exposed as a `commandLineArgs` property source, so `@Value("${app.mode}")` resolves to `fast`. Second, Spring registers an `ApplicationArguments` bean you can `@Autowired` to inspect both option args (`getOptionValues("app.mode")`) and **non-option args** (`getNonOptionArgs()` → `[import, file.csv]`). This is the way to test `CommandLineRunner`/`ApplicationRunner` beans and any startup logic that branches on arguments. Note the command-line property source has lower precedence than `@SpringBootTest(properties=...)` and `@TestPropertySource`, so those can still override an option arg. Non-option args are *not* properties — they're only visible through `ApplicationArguments`.

code

java · 15 lines
java
@SpringBootTest(args = {"--app.mode=fast", "import", "users.csv"})
class StartupArgsTest {

    @Autowired ApplicationArguments args;

    @Value("${app.mode}")
    String mode; // resolved from the option arg

    @Test
    void argumentsAreParsed() {
        assertThat(mode).isEqualTo("fast");
        assertThat(args.getOptionValues("app.mode")).containsExactly("fast");
        assertThat(args.getNonOptionArgs()).containsExactly("import", "users.csv");
    }
}

go deeper

for a junior

Know args is the String[] passed to main/SpringApplication.run.

for a middle

Distinguish option vs non-option args and how each is accessed.

for a senior

Explain precedence of the command-line source and how it drives runners.

for a principal

Reason about add-command-line-properties, testing CLI entrypoints, and when args vs properties is the right tool.

## What it is `args` supplies the `String[]` that a Spring Boot app would normally receive on the command line. `@SpringBootTest` passes it into `SpringApplication.run(...)`, so the test reproduces a real CLI launch. ```java @SpringBootTest(args = {"--app.mode=fast", "--app.retries=2", "import", "users.csv"}) ``` ## Two kinds of arguments Spring Boot classifies each element: - **Option arguments** start with `--` and look like `--key=value` (or bare `--flag`). These are parsed into a property source named `commandLineArgs` and become resolvable properties: `@Value("${app.mode}")`, `environment.getProperty("app.retries")`, `@ConfigurationProperties` binding. - **Non-option arguments** are everything else (`import`, `users.csv`). They are **not** properties. They are only reachable via the `ApplicationArguments` bean. ## ApplicationArguments Spring Boot registers a singleton `ApplicationArguments` bean built from `args`. Inject it to inspect the raw arguments: ```java @Autowired ApplicationArguments appArgs; // appArgs.getOptionNames() -> [app.mode, app.retries] // appArgs.getOptionValues("app.mode") -> [fast] // appArgs.getNonOptionArgs() -> [import, users.csv] // appArgs.containsOption("app.mode") -> true ``` `CommandLineRunner#run(String... args)` receives the raw array; `ApplicationRunner#run(ApplicationArguments args)` receives the parsed object — both are exercised when the context starts, so `args` is how you drive them in a test. ## Precedence The `commandLineArgs` property source is high, but **below** `@SpringBootTest(properties=...)` and `@TestPropertySource`. Ordering (highest first, test-relevant slice): `@DynamicPropertySource` > `@TestPropertySource` > `@SpringBootTest(properties)` > command-line args (`args`) > system properties > OS env > `application.properties`. So an option arg overrides `application.properties`, but an inlined `properties` entry overrides the option arg. ## Gotchas - **Non-option args aren't properties.** `getNonOptionArgs()` is the only way to see `import`/`users.csv`; `@Value("${import}")` will not resolve. - **`addCommandLineProperties`.** By default `SpringApplication.setAddCommandLineProperties(true)`, so option args become a property source. If an app disabled it (`spring.main.add-command-line-properties=false`), option args stop feeding the Environment but `ApplicationArguments` still holds them. - **Format matters.** `--key value` (space) is a *non-option* argument pair, not a property; use `--key=value` for a property. - **Web tests.** With `webEnvironment=RANDOM_PORT` the app still starts via `run(args)`, so `args` are honored the same way. ## When to use Testing `CommandLineRunner`/`ApplicationRunner`, batch/CLI entrypoints, or any bean whose behavior depends on how the process was launched. For plain config overrides prefer `properties = {}` — it's clearer and higher precedence.

  • Can you read `import` (a non-option arg) via `@Value("${import}")`?
    No. Non-option arguments never become properties; they're only accessible through `ApplicationArguments.getNonOptionArgs()` or the raw `String...` in a `CommandLineRunner`.
  • If `application.properties` sets `app.mode=slow` and args pass `--app.mode=fast`, which wins?
    `fast`. Command-line option args have higher precedence than `application.properties`. But an inlined `@SpringBootTest(properties="app.mode=x")` would override even the arg.

saying these in an interview costs you the question

  • Claiming non-option args are exposed as properties
  • Saying `args` has higher precedence than `properties={}`
  • Not knowing `ApplicationArguments` is an injectable bean
  • Using `--key value` and expecting it to bind as a property

context