RSpec-Command

Build Status Gem Version Coverage Gemnasium License

rspec-command is a helper module for using RSpec to test command-line applications.

Quick Start

Add gem 'rspec-command' to your Gemfile and then configure it in your spec_helper.rb:

require 'rspec_command'

RSpec.configure do |config|
  config.include RSpecCommand
end

You can then use the helpers in your specs:

require 'spec_helper'

describe 'myapp' do
  command 'myapp --version'
  its(:stdout) { is_expected.to include('1.0.0') }
end

command

The core helper is command. It takes a command to run and sets it as the subject for the example group. The command can be given as a string, array, or block. If the command is given as an array, no shell processing is done before running it. If the gem you are running inside has a Gemfile, all commands will be run inside a bundle exec. Each command is run in a new temporary directory so the results of one test won't affect others.

command also optionally takes a hash of options to pass through to Mixlib::ShellOut.new. Some common options include :input to provide data on stdin and :timeout to change the execution timeout.

The subject will be set to a Mixlib::ShellOut object so you can use rspec-its to check individual attributes:

describe 'myapp' do
  command 'myapp --version'
  its(:stdout) { is_expected.to include '1.0.0' }
  its(:stderr) { is_expected.to eq '' }
  its(:exitstatus) { is_expected.to eq 0 }
end

file

The file method writes a file in to the temporary directory. You can provide the file content as either a string or a block:

describe 'myapp' do
  command 'myapp read data1.txt data2.txt'
  file 'data1.txt', <<-EOH
a thing
EOH
  file 'data2.txt' do
    "another thing #{Time.now}"
  end
  its(:stdout) { is_expected.to include '2 files imported' }
end

fixture_file

The fixture_file method copies a file or folder from a fixture to the temporary directory:

describe 'myapp' do
  command 'myapp read entries/'
  fixture_file 'entries'
  its(:stdout) { is_expected.to include '4 files imported' }
end

These fixtures are generally kept in spec/fixtures but it can be customized by redefining let(:fixture_root).

environment

The environment method sets environment variables for subprocesses run by command:

describe 'myapp' do
  command 'myapp show'
  environment MYAPP_DEBUG: true
  its(:stderr) { is_expected.to include '[debug]' }
end

match_fixture

The match_fixture matcher lets you check the files created by a command against a fixture:

describe 'myapp' do
  command 'myapp write'
  it { is_expected.to match_fixture 'write_data' }
end

capture_output

The capture_output helper lets you redirect the stdout and stderr of a block of code to strings. This includes any subprocesses or non-Ruby output. This can help with integration testing of CLI code without the overhead of running a full subprocess.

The returned object behaves like a string containing the stdout output, but has stdout, stderr, and exitstatus attributes to simulate the object used by command. exitstatus will always be 0.

describe 'myapp' do
  subject do
    capture_output do
      MyApp::CLI.run('show')
    end
  end
  its(:stdout) { is_expected.to include 'Entry:' }
end

RSpecCommand::Rake

The RSpecCommand::Rake helper is an optional module you can include in your example groups to test Rake tasks without the overhead of running Rake in a full subprocess.

require 'rspec_command'

RSpec.configure do |config|
  config.include RSpecCommand::Rake
end

describe 'rake myapp' do
  rake_task 'myapp'
  rakefile <<-EOH
require 'myapp'
task 'myapp' do
  MyApp.rake_task
end
EOH
  its(:stdout) { is_expected.to include 'Initialized new project' }
end

License

Copyright 2015, Noah Kantrowitz

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.