So golang devs had an internal testing tool called
testscript, for testing... you guessed it, go itself. But like some other go-internal tools, it was so useful that it finally got excised and released into the wild, so that everyone else can use it, and it turns out to be particularly nifty for testing cli programs.
The specific package to "go get" is this one.
Backstory: I had decided to transmogrify my go program into a standalone cli tool. I quickly realized it wasn't obvious how to test the actual, commandline invocations of the executable, for system tests. After a bit of googling, I finally ran into testscript.
In a nutshell, testscript allows you to:
Provide a method (via
testscript.RunMain()) that invokes your
main() application entrypoint, so that it can build an executable out of it (one less step for you to worry about!).
Set some config params (via
testscript.Run) including, crucially, where it should look for test scripts to run.
Be prepared to refactor
main() slightly so that its guts are actually handled by a method that both a hollowed-out main() AND a test script, can call.
These test scripts (which you will write) all end in the suffix
.txtar and have their own mini syntax that testscript understands:
The most trivial of examples:
# expect hello world output and no errors exec echo 'hello, world!' stdout hello, world! ! stderr .
Honestly... it feels so lightweight and straigtforward after dealing with things in js-world (jest, mocha, chai, all the drinks).
That said, I'm sure the go version of such finnicketies will reveal themselves in time, once I start doing anything more involved than sniffing for outcomes.
It took me a really long time to decide on a project structure for my cli program, especially with this introduction of test files and new entry points. Go isn't very rigid about such things, which just adds an air of mystery to things. That said, I did find a basic guide about conventions that seem to have evolved over time. The highlights for me were:
cmdfolder, which more or less holds applications. This is confusing a bit until you realise that a lot of cli programs have sub-commands that are, or could be, applications in and of themselves. Think of docker compose or kubectl apply.
pkgfolder seems to be the equivalent of an open house... it more or less invites others to pilfer the code therein and reuse it elsewhere.
internalfolder is more or less the opposite: for stuff others shouldn't use directly, because the authors aren't expecting it to be used that way / it isn't necessarily designed for external use cases.
So, I think I'm going to go with the following organising principle for now as I only have one app in this repository.
cmd/ |-myapp/ |-subcommand1.go |-subcommand2.go |-main.go testdata/ |-scripts/ |-this.txtar |-that.txtar internal/ |-foo.go |-bar.go myapp.go myapp_test.go
If I discover a better organising principle, I'll be happy to pivot as necessary. Lots of cool discoveries just from running into a need for testscript!