ajalt / clikt

Multiplatform command line interface parsing for Kotlin
https://ajalt.github.io/clikt/
Apache License 2.0
2.54k stars 121 forks source link

Testing commands after parsing option but before running #449

Closed sschuberth closed 9 months ago

sschuberth commented 1 year ago

While reading through https://ajalt.github.io/clikt/testing/#custom-testing I was wondering whether it's easily possible to test the state of a command after parsing has happened and all options have been initialized, but before run() is called. On a related note, in this context it seems a bit odd that parse() does not actually just parse, but also calls run() underneath.

ajalt commented 1 year ago

There is no state in between parsing and running. A command is run as soon as a subcommand token is encountered (before parsing the subcommand's tokens). That way a parent command can print some output or a custom usage error etc. even if there are parse errors further down the line.

sschuberth commented 1 year ago

There is no state in between parsing and running.

Not right now, yes 😀 But I was implying that a refactoring (e.g. something that exposes the canRun parameter to callers) could introduce such a state. But I'm completely fine if you deem this to not be feasible, and close this issue!

ajalt commented 1 year ago

What's the use case? If you disable running for tests, it seems like you aren't testing your app, you're just testing Clikt.

sschuberth commented 1 year ago

I guess you're half-right, I'd be testing the configuration of clikt within my app. The concrete use-case arose when fixing this bug in our app where a default value for the rulesFile option was always applied, but that should only happen if rulesResource is not given. In a test that has access to the options but does not run the command I could have simply verified that rulesFile is null if rulesResource is set.

pedroteixeira commented 1 year ago

Also would like to re-use an existing command class in tests, but calling a different method instead of run, but using the same state option values.

Basically, if it's possible to instantiate the command class and set the option values by code? instead of using .parse

Any hints how to achieve? thanks

ajalt commented 1 year ago

@pedroteixeira no, I would suggest using parse

pedroteixeira commented 1 year ago

Ok, I think one way is to actually add an option such as "dryRun" and handle in the run() method.

But please let us know if there any tricks (perhaps with .context { } ?) that would allow one to override / define values for the option delegates by code :pray: (without need to using current parse) -- but I understand I am probably just thinking about the simple option cases (no sub commands, etc)

ajalt commented 9 months ago

If you need to set values from test code without changing your commands or writing a command line, you can set a valueSource in your tests. Something like:

val testValues = MapValueSource(mapOf("my-option" to "value"))
MyCommand().context { valueSource = testValues }.parse(emptyList())

I don't plan on building in a dry-run capability, so I'll close this issue.