0
# Testing Utilities
1
2
Test command execution with controlled input/output, environment variables, and terminal settings for comprehensive CLI testing.
3
4
## Capabilities
5
6
### CliktCommandTestResult
7
8
Data class containing the results of a test execution, including all output streams and exit status.
9
10
```kotlin { .api }
11
/**
12
* Result of testing a CliktCommand
13
* @param stdout Content written to standard output
14
* @param stderr Content written to standard error
15
* @param output Combined stdout and stderr content
16
* @param statusCode Exit status code (0 for success, non-zero for errors)
17
*/
18
data class CliktCommandTestResult(
19
val stdout: String,
20
val stderr: String,
21
val output: String, // stdout + stderr combined
22
val statusCode: Int
23
)
24
```
25
26
**Usage Examples:**
27
28
```kotlin
29
class HelloCommand : CliktCommand() {
30
private val name by argument()
31
override fun run() = echo("Hello $name!")
32
}
33
34
val result = HelloCommand().test("World")
35
assertEquals("Hello World!\n", result.stdout)
36
assertEquals("", result.stderr)
37
assertEquals(0, result.statusCode)
38
```
39
40
### Test Function
41
42
Execute a command in a controlled test environment with configurable input, environment, and terminal settings.
43
44
```kotlin { .api }
45
/**
46
* Test a CliktCommand with controlled environment
47
* @param argv Command line arguments as a single string
48
* @param stdin Standard input content
49
* @param envvars Environment variables to set
50
* @param includeSystemEnvvars Include system environment variables
51
* @param ansiLevel ANSI color support level
52
* @param width Terminal width in characters
53
* @param height Terminal height in lines
54
* @return CliktCommandTestResult containing all output and status
55
*/
56
fun CliktCommand.test(
57
argv: String,
58
stdin: String = "",
59
envvars: Map<String, String> = emptyMap(),
60
includeSystemEnvvars: Boolean = false,
61
ansiLevel: AnsiLevel = AnsiLevel.NONE,
62
width: Int = 79,
63
height: Int = 24
64
): CliktCommandTestResult
65
66
fun CliktCommand.test(
67
vararg argv: String,
68
stdin: String = "",
69
envvars: Map<String, String> = emptyMap(),
70
includeSystemEnvvars: Boolean = false,
71
ansiLevel: AnsiLevel = AnsiLevel.NONE,
72
width: Int = 79,
73
height: Int = 24
74
): CliktCommandTestResult
75
76
fun CliktCommand.test(
77
argv: List<String>,
78
stdin: String = "",
79
envvars: Map<String, String> = emptyMap(),
80
includeSystemEnvvars: Boolean = false,
81
ansiLevel: AnsiLevel = AnsiLevel.NONE,
82
width: Int = 79,
83
height: Int = 24
84
): CliktCommandTestResult
85
86
fun CliktCommand.test(
87
argv: Array<String>,
88
stdin: String = "",
89
envvars: Map<String, String> = emptyMap(),
90
includeSystemEnvvars: Boolean = false,
91
ansiLevel: AnsiLevel = AnsiLevel.NONE,
92
width: Int = 79,
93
height: Int = 24
94
): CliktCommandTestResult
95
```
96
97
**Usage Examples:**
98
99
```kotlin
100
class ConfigCommand : CliktCommand() {
101
private val config by option("--config", envvar = "CONFIG_FILE")
102
override fun run() = echo("Config: $config")
103
}
104
105
// Test with arguments
106
val result1 = ConfigCommand().test("--config myconfig.yml")
107
assertEquals("Config: myconfig.yml\n", result1.stdout)
108
109
// Test with environment variables
110
val result2 = ConfigCommand().test("", envvars = mapOf("CONFIG_FILE" to "env.yml"))
111
assertEquals("Config: env.yml\n", result2.stdout)
112
113
// Test with stdin
114
class ReadCommand : CliktCommand() {
115
override fun run() {
116
val input = readLine()
117
echo("Read: $input")
118
}
119
}
120
121
val result3 = ReadCommand().test("", stdin = "test input\n")
122
assertEquals("Read: test input\n", result3.stdout)
123
```
124
125
## Testing Patterns
126
127
### Basic Command Testing
128
129
Test simple commands with arguments and options:
130
131
```kotlin
132
class GreetCommand : CliktCommand() {
133
private val greeting by option("-g", "--greeting").default("Hello")
134
private val name by argument()
135
136
override fun run() = echo("$greeting $name!")
137
}
138
139
@Test
140
fun testGreeting() {
141
val result = GreetCommand().test("World")
142
assertEquals("Hello World!\n", result.stdout)
143
assertEquals(0, result.statusCode)
144
}
145
146
@Test
147
fun testCustomGreeting() {
148
val result = GreetCommand().test("--greeting Hi Alice")
149
assertEquals("Hi Alice!\n", result.stdout)
150
}
151
```
152
153
### Error Testing
154
155
Test error conditions and validate error messages:
156
157
```kotlin
158
class RequiredOptionCommand : CliktCommand() {
159
private val required by option("--required").required()
160
override fun run() = echo("Value: $required")
161
}
162
163
@Test
164
fun testMissingRequired() {
165
val result = RequiredOptionCommand().test("")
166
assertTrue(result.statusCode != 0)
167
assertTrue(result.stderr.contains("Missing option"))
168
}
169
```
170
171
### Environment Variable Testing
172
173
Test environment variable integration:
174
175
```kotlin
176
class EnvCommand : CliktCommand() {
177
private val value by option("--value", envvar = "TEST_VALUE")
178
override fun run() = echo("Value: $value")
179
}
180
181
@Test
182
fun testEnvironmentVariable() {
183
val result = EnvCommand().test("", envvars = mapOf("TEST_VALUE" to "from-env"))
184
assertEquals("Value: from-env\n", result.stdout)
185
}
186
```
187
188
### Subcommand Testing
189
190
Test commands with subcommands:
191
192
```kotlin
193
class MainCommand : CliktCommand() {
194
override fun run() = Unit
195
}
196
197
class SubCommand : CliktCommand(name = "sub") {
198
private val option by option("--opt")
199
override fun run() = echo("Sub: $option")
200
}
201
202
@Test
203
fun testSubcommand() {
204
val main = MainCommand()
205
main.subcommands(SubCommand())
206
207
val result = main.test("sub --opt value")
208
assertEquals("Sub: value\n", result.stdout)
209
}
210
```
211
212
### Terminal Width Testing
213
214
Test help output formatting with different terminal widths:
215
216
```kotlin
217
class HelpCommand : CliktCommand(help = "A command with a very long help description that will wrap at different terminal widths")
218
219
@Test
220
fun testNarrowTerminal() {
221
val result = HelpCommand().test("--help", width = 40)
222
assertTrue(result.stdout.contains("A command with a very"))
223
}
224
225
@Test
226
fun testWideTerminal() {
227
val result = HelpCommand().test("--help", width = 120)
228
assertTrue(result.stdout.contains("A command with a very long help description"))
229
}
230
```
231
232
### Interactive Input Testing
233
234
Test commands that read from stdin:
235
236
```kotlin
237
class InteractiveCommand : CliktCommand() {
238
override fun run() {
239
echo("Enter name:")
240
val name = readLine()
241
echo("Hello $name!")
242
}
243
}
244
245
@Test
246
fun testInteractive() {
247
val result = InteractiveCommand().test("", stdin = "Alice\n")
248
assertTrue(result.stdout.contains("Enter name:"))
249
assertTrue(result.stdout.contains("Hello Alice!"))
250
}
251
```
252
253
## Advanced Testing Features
254
255
### ANSI Color Testing
256
257
Test colored output with different ANSI support levels:
258
259
```kotlin
260
import com.github.ajalt.mordant.rendering.AnsiLevel
261
262
@Test
263
fun testColorOutput() {
264
val colorResult = command.test("", ansiLevel = AnsiLevel.TRUECOLOR)
265
val noColorResult = command.test("", ansiLevel = AnsiLevel.NONE)
266
267
// Colored output will contain ANSI escape sequences
268
assertTrue(colorResult.stdout.contains("\u001B["))
269
270
// No-color output will not contain escape sequences
271
assertFalse(noColorResult.stdout.contains("\u001B["))
272
}
273
```
274
275
### System Environment Testing
276
277
Control whether system environment variables are included:
278
279
```kotlin
280
@Test
281
fun testSystemEnvExclusion() {
282
// Test with only specified environment variables
283
val result = command.test("",
284
envvars = mapOf("TEST_VAR" to "test"),
285
includeSystemEnvvars = false
286
)
287
288
// System PATH variable should not be available
289
assertFalse(result.stdout.contains(System.getenv("PATH") ?: ""))
290
}
291
```
292
293
## Best Practices
294
295
### Test Organization
296
297
```kotlin
298
class CommandTest {
299
private lateinit var command: MyCommand
300
301
@BeforeEach
302
fun setup() {
303
command = MyCommand()
304
}
305
306
@Test
307
fun testSuccessCase() {
308
val result = command.test("valid arguments")
309
assertEquals(0, result.statusCode)
310
assertTrue(result.stderr.isEmpty())
311
}
312
313
@Test
314
fun testErrorCase() {
315
val result = command.test("invalid arguments")
316
assertNotEquals(0, result.statusCode)
317
assertTrue(result.stderr.isNotEmpty())
318
}
319
}
320
```
321
322
### Assertion Helpers
323
324
```kotlin
325
fun assertSuccess(result: CliktCommandTestResult) {
326
assertEquals(0, result.statusCode, "Command should succeed")
327
assertTrue(result.stderr.isEmpty(), "No error output expected")
328
}
329
330
fun assertError(result: CliktCommandTestResult, expectedMessage: String) {
331
assertNotEquals(0, result.statusCode, "Command should fail")
332
assertTrue(result.stderr.contains(expectedMessage),
333
"Expected error message not found: $expectedMessage")
334
}
335
```
336
337
The testing utilities provide comprehensive support for testing CLI applications with full control over the execution environment, making it easy to verify command behavior, error handling, and output formatting across different scenarios.