0

I'm trying to create a Ruby command line interface with the following options:

ruby cli.rb -g  # Prints a list of people sorted by gender
ruby cli.rb -n  # Prints a list of people sorted by last name
ruby cli.rb -b  # Prints a list of people sorted by birth date
ruby cli.rb -a LastName FirstName DateOfBirth  #  Add a new person to the list of people

I have already created a Directory class that stores a list of people and also has methods to sort people and add people. The next step is to build a CLI around this class.

How can I create this in a way that can be tested with RSpec? My initial attempt involved a looped gets.chomp instead of running a Ruby file with flags, but this loop does not play well with RSpec. The examples I've found online just use Ruby's OptionParser in a simple script and not inside an actual class. Also, how would I handle creating ARGV parameters in RSpec? I'm just looking for tips on the general structure of this CLI class so that I can write tests.

I do not want to use any gems for this.

user3809888
  • 377
  • 1
  • 6
  • 23

1 Answers1

0

I'm not sure what the context of your problem is, so I'm just going to give you a little brain dump, the merit of which is yours to determine:

  1. A cold hard fact: In the wild, I've seen few people write unit tests for command line arguments. The reason for this probably has to do with the fact that a well-written command line tool is a lot like a very skinny router (in the MVC sense). It looks at the arguments, and routes them to the proper function (eg, "-g" routes to something like MyObject.print_by_gender). And if you use a battle-tested options parser (like OptionParser) on the incoming side and a well-tested piece of code that the router calls (eg, MyObject.print_by_gender, assuming that's the name of a function, and that you've tested it well), then there's very little to test in the command line file itself.
  2. That context in place, let's consider your problem. You want to test an object that interacts with the external world in 2 ways. Namely, stdin and stdout. This means that you could create an integration-style test, and actually pass thing in on stdin and test the output the command creates on stdout - as you mentioned - or you can mock stdin and stdout. In my experience, mocking external things like stdin and stdout are a better approach. That said, I'll recommend these two SO questions/answers to explain how to mock STDIN (this and this).
Community
  • 1
  • 1
thataustin
  • 2,035
  • 19
  • 18