Question

The following scenario pretty much sums up my problem:

Scenario: problems with subprocesses
  Given the date is 01/01/2012 10:31
  When I run `ruby -e "puts Time.now"`
  Then the output should contain "10:31"

It boils down to When I run ruby -e "puts Time.now" launching a child process and thus making all of my Timecop.freeze stubs ineffective, since they only work on the main process. I need to somehow 'inject' the current context into the command that is run, but I seem to be unable to come up with anything. Am I trying something impossible here?


The step:

require 'timecop'
Given /^the date is (\d+)\/(\d+)\/(\d+) (\d+):(\d+)$/ do |month, day, year, hour, minute|
  Timecop.freeze(Time.local(year.to_i, month.to_i, day.to_i, hour.to_i , minute.to_i, 0))
end
Was it helpful?

Solution

I see 2 options here.

  1. Create a hook in the system under test that you can use to inject the required context, in this example you could add a 'current time' parameter to the command line app that you're testing. Alternatively specify a fixed time in the apps config file, or database, there are many options. This presumes that you own the app that you're testing and can make such changes. This need only be a very small change, e.g. if the 'current time' parameter is specified, then use Timecop to freeze the time at that time.

  2. Find some way to bring the system under test into the Cucumber process. In your example instead of shelling out to ruby -e "puts Time.now", you could instance_eval "puts Time.now". More realistically, you could require the app, and if the command-line runner is simply a thin wrapper around some classes which actually do the work, then you can call those directly. Alternatively, you might be able to populate whatever ARGV parameters are expected, mock out the puts method, and just require the file, which should give you a close experience to running it out of process, but you'd be able to use Timecop effectively.

OTHER TIPS

Aruba also supports a feature called in process execution which allows you to run everything in one process. However, it pretty much exclusively restricts you to testing Ruby CLIs with Aruba, since the way it works is by overriding the default behavior of shelling out to a command with calling a Ruby method.

Here is what it looks like for me - I am using Aruba for testing a Thor application:

require 'aruba/cucumber'
require 'aruba/in_process'
require 'thor'
require 'thor/runner'

load 'Thorfile'

class ThorFriendlyMain
  def initialize(argv, stdin, stdout, stderr, kernel)
    @argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel
  end

  def execute!
    $stdin = @stdin
    $stdout = @stdout
    $stderr = @stderr
    Thor::Runner.start(@argv)
    $stdin = STDIN
    $stdout = STDOUT
    $stderr = STDERR
  end   
end     

Aruba.config.command_launcher = :in_process
Aruba.config.main_class = ThorFriendlyMain
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top