Carlton Whitehead
10/09/2021, 8:05 PMrequireObject()
to pull in state from parent commands. Ideally, I'd like to install these through the context builder. Example code in thread.class Subcommand : CliktCommand() {
private val environment: Environment by requireObject()
override fun run() {
echo(environment.value)
}
}
class SubcommandTest : CliktCommand() {
lateinit var subcommand: Subcommand
lateinit var testConsole: StringBufferConsole
@BeforeEach
fun beforeEach() {
testConsole = StringBufferConsole()
subcommand = Subcommand()
.context {
console = testConsole
obj = Environment(value = "test")
// but there is no `obj` available in Context.Builder
}
}
@Test
fun `It should echo environment value`() {
subcommand.parse(emptyArray())
assertThat(testConsole.output).contains("test")
}
}
I tried setting it through subcommand.currentContext.obj
during beforeEach()
, but accessing the context prior to parse is not allowed. For the time being, I've resorted to adding constructor parameters to each of my subcommands to allow the unit tests to override the environment.
class SubcommandTest(forceEnvironment: Environment? = null) : CliktCommand() {
private val environment: Environment by requireObject()
override fun run() {
val useEnvironment = forceEnvironment ?: environment
echo(useEnvironment.value)
}
}
I'm not too fond of this approach because it adds complexity to main code simply for test concerns. It feels like a missing feature that there isn't a way to pass an obj
value into the context from the context builder. Have I missed something here?
Is there a better way I should approach this instead?AJ Alt
10/10/2021, 4:13 PMclass Parent : CliktCommand() {
override fun run() {
currentContext.obj = Environment("test")
}
}
@Test
fun `It should echo environment value`() {
Parent().context { console = testConsole }
.subcommands(Subcommand())
.parse(emptyArray())
assertThat(testConsole.output).contains("test")
}
obj
setter to the context builder also seems like a reasonable feature.Carlton Whitehead
10/10/2021, 5:02 PMobj
values, I could see this being a preferred approach.
Another option I've since tried is a singleton model. Write the value in the root command, and then read the value in subcommands. This leads to quite a lot of constructor boilerplate throughout an app with many subcommands but otherwise works out pretty nicely for my use case.
My preference remains for there to be an obj
available in the Context.Builder
. I'll send you a PR adding support for that.