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
I see 2 options here.
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.
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.