Testing a command-line tool with plain Ruby
I wanted to write some system tests that invoked the command line tool and checked the results. As long as I can keep them fast, I love system tests that test the real thing from top to bottom.
It uses the QuantConnect API under the hood. It is HTTP-based, so I could use VCR and not having to mock the API connection for testing purposes. I love VCR, so this was an easy choice.
For testing commands execution, I first tried to use aruba. I had learned about it in this book and I knew RSpec used it for its tests. Unfortunately, I found a blocking issue: for VCR (or any mocking approach) to work, I needed to use the
in_process approach, but this mode wouldn’t work with running commands interactively. I needed to exercise how the user interacted with the tool for a couple of cases, so this invalidated aruba for me.
Looking for an alternative, I ended up just using plain Ruby and writing a few simple helpers for my needs:
- For testing the output, MiniTest’s
capture_iolets you capture
- For testing the input, you can just stub the methods you need in
$stdin. See this article.
def test_init_asks_for_project_and_store_it_in_settings_with_default_extension sign_in type_when_prompted '2', '' do run_command 'qc init' end assert_match /My first C# project/, last_command.output assert_stored_project_settings project_id: '799895', file_extensions: 'cs,py' assert_equal 0, last_command.exit_status end
For dealing with files, I liked aruba’s approach of creating a fixtures directory with files you can use to prepare your tests. Ruby support for scripting with files is wonderful, so it was easy to prepare another helper that allowed me to write tests like this:
class PushTest < SystemTest def setup prepare_local_files 'file_1.cs', 'file_2.cs' ... end def test_initial_push_should_push_all_the_files run_command 'qc push' assert_match /Uploading file_1.cs.../, last_command.output assert_match /Uploading file_2.cs.../, last_command.output assert_files_were_uploaded 'file_1.cs', 'file_2.cs' end ... end
I remember that in my early days with Ruby and Rails I was willing to use whatever gem that more or less matched my needs. These days I fight a lot before adding new libraries. This was a good reminder to check how vanilla works for you before adding third-party solutions.