# I am the Watcher. I am your guide through this vast new twtiverse.
# 
# Usage:
#     https://watcher.sour.is/api/plain/users              View list of users and latest twt date.
#     https://watcher.sour.is/api/plain/twt                View all twts.
#     https://watcher.sour.is/api/plain/mentions?uri=:uri  View all mentions for uri.
#     https://watcher.sour.is/api/plain/conv/:hash         View all twts for a conversation subject.
# 
# Options:
#     uri     Filter to show a specific users twts.
#     offset  Start index for quey.
#     limit   Count of items to return (going back in time).
# 
# twt range = 1 33
# self = https://watcher.sour.is/conv/kxlpuda
Writing and running full e2e integration tests using Go and for Go CLI applications. Lookingo into one of:

google/go-cmdtest: This package simplifies testing of command-line interfaces.
Writing and running full e2e integration tests using Go and for Go CLI applications. Lookingo into one of:

google/go-cmdtest: This package simplifies testing of command-line interfaces.
Or:

rendon/testcli: Go lang testing framework for command line applications.
Or:

rendon/testcli: Go lang testing framework for command line applications.
Or:

lucapette/go-cli-integration-tests
Or:

lucapette/go-cli-integration-tests
@lyse or @movq Can I get your thoughts and opinions on any of these three options for testing CLI apps written in Go with the possibility of also measuring coverage -- Which means using the test binary under test which I _think_ all three solutions can do? (definitely the first one says it can).
@lyse or @movq Can I get your thoughts and opinions on any of these three options for testing CLI apps written in Go with the possibility of also measuring coverage -- Which means using the test binary under test which I _think_ all three solutions can do? (definitely the first one says it can).
@prologic Sorry, haven't used any of them.
Quickly tried out testcli and it's a "no go" for me:

- Its README is out-of-date and has an old reference to a package that had its import path changed (easily fixed)
- Running the tests failed miserably as it could not find the greeting binary in the $PATH πŸ€¦β€β™‚οΈ

da fuq?! I guess this doesn't do what I _thought_ -- which is to build the test binary and use that to run CLI tests against so you can actually measure coverage πŸ˜”

Next!
Quickly tried out testcli and it's a "no go" for me:

- Its README is out-of-date and has an old reference to a package that had its import path changed (easily fixed)
- Running the tests failed miserably as it could not find the greeting binary in the $PATH πŸ€¦β€β™‚οΈ

da fuq?! I guess this doesn't do what I _thought_ -- which is to build the test binary and use that to run CLI tests against so you can actually measure coverage πŸ˜”

Next!
[prologic/greeting: A "Hello World" greeting command-line (CLI) application - greeting - Mills](https://git.mills.io/prologic/greeting)

This is eventually what I came up with so far... What do you think @lyse ? πŸ€”
[prologic/greeting: A "Hello World" greeting command-line (CLI) application - greeting - Mills](https://git.mills.io/prologic/greeting)

This is eventually what I came up with so far... What do you think @lyse ? πŸ€”
@prologic Haven’t used them so far, either, sorry. πŸ€”
@prologic Haven’t used them so far, either, sorry. πŸ€”
@prologic Haven’t used them so far, either, sorry. πŸ€”
@prologic Haven’t used them so far, either, sorry. πŸ€”
That's quite a complicated hello world, @prologic :-D

My main()s often also just do os.Exit(run()). Passing the command line arguments run run(…) seems like another obvious thing to do. Even though, I should have done this more consistently, I reckon. I feel very stupid now, because for whatever reason, it never occurred to me to simply pass an io.Writer for stdout. I really like that. Although, I'm wondering why there's nothing for stderr. Errors should definitely not mix with other output in my opinion. Anyways. When testing, I always captured stdout with a much more complicated code segment so far. Store the original output and error streams, set the new ones, execute the test, convert things to strings and finally reset to the original streams. I will definitely adopt these io.Writer arguments. Thanks, mate! :-)

This cmdtest test execution also captures the coverage? It looks kinda more complicated than it should be, otherwise. Just a program with the test definition file would be enough in my opinion.

I don't know if I like the concept of providing a single test definition file or not. It's a bit intriguing, but I fear that's not flexible enough. Just a gut feeling, might be wrong.
@lyse Yeah I actually use this technique a lot in GoNix for basically all the Applets. I _think_ this makes it easier to test. The cmdtest package is kind of cool though really, it basically implements the same kind of test runner as you may (or may not) have seen in the Mercurial test suite. The test files in testdata are essentially text files that look a bit like you've run something on the console and copied pasted them. This is brilliant for e2e cli integration testing πŸ‘Œ And yes it manages to run the test binary so that coverage can also be measured which is fantastic πŸ‘Œ -- Of course this does not preclude you from writing unit tests for any other parts of your package/library that have a public facing API -- But if your public facing API is _just_ the CLI then this is a perfect fit πŸ‘Œ
@lyse Yeah I actually use this technique a lot in GoNix for basically all the Applets. I _think_ this makes it easier to test. The cmdtest package is kind of cool though really, it basically implements the same kind of test runner as you may (or may not) have seen in the Mercurial test suite. The test files in testdata are essentially text files that look a bit like you've run something on the console and copied pasted them. This is brilliant for e2e cli integration testing πŸ‘Œ And yes it manages to run the test binary so that coverage can also be measured which is fantastic πŸ‘Œ -- Of course this does not preclude you from writing unit tests for any other parts of your package/library that have a public facing API -- But if your public facing API is _just_ the CLI then this is a perfect fit πŸ‘Œ
So... This PR I cut this morning adds the basic structure for e2e cli integration tests as seen in my fancy "Hello World" greeting πŸ˜… -- What I like here is how straight forward it was to do πŸ‘Œ
So... This PR I cut this morning adds the basic structure for e2e cli integration tests as seen in my fancy "Hello World" greeting πŸ˜… -- What I like here is how straight forward it was to do πŸ‘Œ
@lyse Oh btw... The reason a io.Writer is left out for "stderr" is that normally I tend to just set the logging output to os.Stderr, like log.SetOutput(os.Stderr) anyway and its not usually something I end up testing. Not sure if this is the best approach, but I'm only really interested in testing the "output"(s) and either error or non-error cases.
@lyse Oh btw... The reason a io.Writer is left out for "stderr" is that normally I tend to just set the logging output to os.Stderr, like log.SetOutput(os.Stderr) anyway and its not usually something I end up testing. Not sure if this is the best approach, but I'm only really interested in testing the "output"(s) and either error or non-error cases.
@prologic Logging is different, I meant regular errors messages. E.g. you invoke the program with an invalid argument or something else goes wrong. That should then be reported on stderr and not stdout. When striving for a good coverage rate, error cases should not be forgotten in my opinion. Ideally, error messages are tested, too. I've seen a bunch of cases in the past, where something was broken, because there weren't any tests. But to be fair, I neglect them most of time, too. :-( Just checked, go-cmdtest merges both stdout and -err, that's a no-go in my books.
@prologic have you ever tried expect
@tkanos Yes a long time ago
@tkanos Yes a long time ago
@lyse Yeah I agree errors should be tested too. That's why Main() has the func Main(w io.Writer, args []string) error πŸ˜… So you _can_ actually assert on the error returned. For a CLI however, I'm not particularly a fan of logging errors to stderr too much (if at all). And re go-cmdtest FWIW a Terminal combines stdout and stderr too by default when displaying the output of a program πŸ˜… -- However I filed an issue against the cmdtest project and now I'm not so sure I want to continue using it, I _may_ as well just figure out how to run the test binary under coverage and write the tests myself in Go.
@lyse Yeah I agree errors should be tested too. That's why Main() has the func Main(w io.Writer, args []string) error πŸ˜… So you _can_ actually assert on the error returned. For a CLI however, I'm not particularly a fan of logging errors to stderr too much (if at all). And re go-cmdtest FWIW a Terminal combines stdout and stderr too by default when displaying the output of a program πŸ˜… -- However I filed an issue against the cmdtest project and now I'm not so sure I want to continue using it, I _may_ as well just figure out how to run the test binary under coverage and write the tests myself in Go.
@prologic Whoops, I must have missed the error return value! That sounds good to me. When there are just fatal errors that abort the program execution, a main function returning an error is definitely enough.

Hmm, if you don't want to report errors to stderr, where do you write them to? Hopefully not stdout. A log file? It obviously depends on the program and such, but generally I do not want to dig up errors from a log file. Usually, I find it much more convenient to see them directly. Properly dealing with stdout and stderr basically provides the capabilities for free to be pipeline-ready. And of course, -q or something along those lines is also a good choice. When talking about more serious programs, that is. Not just some quickly cobbled together helper.
@lyse Hmmm good points but yeah it depends on the program πŸ‘Œ
@lyse Hmmm good points but yeah it depends on the program πŸ‘Œ