Testing Thor Apps with Cucumber
July 08, 2013
Imagine you’re writing a command-line Ruby tool using Thor. Although probably if you’re reading this, you really are. Probably you would like to have nice test coverage with a bunch of integration tests. You might even want to write them in Cucumber (God save your soul!) for some reason. You’d google around and find Aruba - a Cucumber extension for testing command-line applications. You’d write some tests and it would work. Cool, huh?
The story could end here, but if you care about the speed of your tests, you need to go deeper. By default, Aruba runs your commands in a separate process, loading the whole command-line apps separately. Isolation is nice, but when you have 50+ tests you might want to omit the step of spawning a new process and loading the app. That’s how you’d do it:
# aruba_extension.rb
module MyFancyTool
module TestSupport
module ArubaExt
def with_redirected_stdout(&block)
redirect_stdout
yield
bring_back_stdout
end
def mock_stdout
unescape @stdout_cache
end
def mock_stderr
unescape @stderr_cache
end
def mock_output
mock_stdout + mock_stderr
end
private
def redirect_stdout
@stdout_cache = ''
@stderr_cache = ''
@stdout_redirected = true
@orig_stdout = $stdout
@orig_stderr = $stderr
$stdout = @mock_stdout = StringIO.new
$stderr = @mock_stderr = StringIO.new
end
def bring_back_stdout
@stdout_cache = @mock_stderr.string
@stderr_cache = @mock_stdout.string
@stdout_redirected = false
$stdout = @orig_stdout
$stderr = @orig_stderr
@orig_stdout = @mock_stdout = nil
@orig_stderr = @mock_stderr = nil
end
end
end
end
World(MyFancyTool::TestSupport::ArubaExt)
# basic_steps.rb
When /^I run my fancy tool with command "(.*)"$/ do |args|
args = args.split
args[0] = args[0].to_sym
Dir.chdir(current_dir) do
with_redirected_stdout do
MyFancyTool::MyCLI.start args
end
end
end
Then /^the last output should match (#{PATTERN})$/ do |expected|
assert { mock_output =~ expected }
end
# example.feature
Feature: Reassuring awesomeness of MyFancyTool.
Scenario: Running the tool
When I run my fancy tool with command "do stuff"
Then the last output should match /awesome output/
You see that we run Ruby command-line commands in the Aruba process itself by calling MyCLI.start
by hand and capturing standard output. Then we can match stuff against saved cache of the output to see if it’s what we expected it to be.
In my case it reduced the time to run all the features by a whopping 60%.
That’s all folks, stay covered!
Written by Wojciech Ogrodowczyk who takes photos, climbs mountains, and runs Brains & Beards to help companies deliver better mobile applications faster.