Jamaica
I spend a lot of time writing CLI applications, which means that I spend a lot of time testing CLI applications. I’m also one of those folks that insists that Cucumber is awesome, particularly in terms of testing CLI apps with Aruba. I actually use Aruba so heavily that I used a combination of it, Cucumber, and some stupid Rake tricks to test the first several Go apps that I wrote. This was a bit problematic, though …
The Beach Boys Lied
One of the reasons that I like Aruba, aside from the bit where it provides some really handy CLI-centric helpers, is that it allows me to test my app in-process. That is, rather than spawning the app under test via exec
or what have you, a Ruby app with a very specific invocation style can be tested in the same Ruby process as the test suite. While that can be used to do some things that should never be done (like mocking the actual code under test), it also makes it incredibly convenient to write reasonably-mocked integration tests.
The problem with using Cucumber and Aruba for testing Go apps is that, as one might expect, you can’t run the app under test in-process. That being the case, doing even simple mocking for external services is really, really difficult to pull off (nevermind the fact that you can’t get any code coverage information for these tests). So, what to do?
Change of Venue
Since we’re writing a Go app, it makes sense to use Go testing tools. While I do adore the built-in testing
package for package-centric unit/behavior tests, I still think something like Cucumber is better at the user-focused tests. Luckily, the folks at DATA-DOG feel the same way, so they created godog, a quasi-official Cucumber implementation for Go. That’s awesome!
But there’s no Aruba for Go. To that end, I’ve just released jamaica v1.0.2. As a fun aside, when I first started working on jamaica, I’d decided to rename it to “keylargo,” but I apparently forgot about this along the way.
It’s Minimal
While Aruba provides a ton of step definitions and configuration options, jamaica provides a fairly minimal set of each (mostly just because it fits my needs as it stands right now):
Configuration
The only real configuration option for jamaica is the command to test. I use cobra pretty heavily in my Go work, so the object that you pass into jamaica.SetRootCmd()
pretty much has to quack like a cobra.Command
in two ways: it must implement this interface:
SetArgs(args []string)
for passing CLI argumentsExecute() error
for executing the command
Steps
So far as step definitions (injected into your godog suite with jamaica.StepUp(yoursuite)
) go, jamaica provides the following:
-
# Run the command When I run `somecommand with its args`
-
# Assert that the command exited cleanly Then it exits successfully
-
# Assert that the command exited with an error Then it exits with an error
-
# Assert that the command output contains a given string Then stdout contains "a given string"
-
# Assert that the command output is exactly a given string Then stdout is "a given string"
Caveats and Conclusions
I’m pretty happy with the way that jamaica works. The one bit that I find problematic is the way that the “I run” step captures the output. For one, it only captures STDOUT, not STDERR. Also, due to the way the capture is done (basically mocking os.Stdout
), I’m pretty sure that this thing is not at all safe for concurrent test suites. That said, I’m totally open to pull requests, and I’ve started noodling out how to get around both of those problems.
Enjoy!