RSpec is coming in for acceptance tests. More details follow...

RSpec was chosen to be used for acceptance tests for many reasons:

* The tests are actually much cleaner now. It is clearer to see what
  is being tested, and what is being used for setup.
* Matcher transition will be coming soon. This will really clean up
  a lot of the "assert" boilerplate all over. There was a lot of repetition
  in this area.
* Shared examples will help greatly for testing common error cases
  for many commands.
* The test runner for RSpec is simply much better. Being able to specify
  the exact test to run by line, for example, is a great help.
This commit is contained in:
Mitchell Hashimoto 2011-11-06 23:47:23 -08:00
parent 699c67c1aa
commit 87bc6ec63f
15 changed files with 124 additions and 107 deletions

View File

@ -1,6 +1,7 @@
require 'rubygems' require 'rubygems'
require 'bundler/setup' require 'bundler/setup'
require 'rake/testtask' require 'rake/testtask'
require 'rspec/core/rake_task'
Bundler::GemHelper.install_tasks Bundler::GemHelper.install_tasks
task :default => "test:unit" task :default => "test:unit"
@ -12,9 +13,8 @@ namespace :test do
t.pattern = "test/unit/**/*_test.rb" t.pattern = "test/unit/**/*_test.rb"
end end
Rake::TestTask.new do |t| RSpec::Core::RakeTask.new do |t|
t.name = "acceptance" t.name = "acceptance"
t.libs << "test/acceptance"
t.pattern = "test/acceptance/**/*_test.rb" t.pattern = "test/acceptance/**/*_test.rb"
end end
end end

View File

@ -1,11 +1,11 @@
require "rubygems" require "rubygems"
require "contest" require "rspec/autorun"
require "log4r" require "log4r"
require File.expand_path("../helpers/config", __FILE__) require File.expand_path("../support/base_context", __FILE__)
require File.expand_path("../helpers/isolated_environment", __FILE__) require File.expand_path("../support/config", __FILE__)
require File.expand_path("../helpers/output", __FILE__) require File.expand_path("../support/virtualbox", __FILE__)
require File.expand_path("../helpers/virtualbox", __FILE__)
# If VirtualBox is currently running, fail. # If VirtualBox is currently running, fail.
if Acceptance::VirtualBox.find_vboxsvc if Acceptance::VirtualBox.find_vboxsvc
@ -32,55 +32,7 @@ end
$acceptance_options = Acceptance::Config.new(ENV["ACCEPTANCE_CONFIG"]) $acceptance_options = Acceptance::Config.new(ENV["ACCEPTANCE_CONFIG"])
class AcceptanceTest < Test::Unit::TestCase # Configure RSpec
# This method is a shortcut to give access to the global configuration RSpec.configure do |c|
# setup by the acceptance tests. c.expect_with :stdlib
def config
$acceptance_options
end
# Executes the given command in the isolated environment. This
# is just a shortcut to IsolatedEnvironment#execute.
#
# @return [Object]
def execute(*args, &block)
@environment.execute(*args, &block)
end
# This is a shortcut method to instantiate an Output matcher.
#
# @return [Acceptance::Output]
def output(text)
Acceptance::Output.new(text)
end
# This method is an assertion helper for asserting that a process
# succeeds. It is a wrapper around `execute` that asserts that the
# exit status was successful.
def assert_execute(*args, &block)
result = execute(*args, &block)
assert(result.success?, "expected '#{args.join(" ")}' to succeed")
result
end
setup do
# Wait for VBoxSVC to disappear
Acceptance::VirtualBox.wait_for_vboxsvc
# Setup the environment so that we have an isolated area
# to run Vagrant. We do some configuration here as well in order
# to replace "vagrant" with the proper path to Vagrant as well
# as tell the isolated environment about custom environmental
# variables to pass in.
apps = { "vagrant" => config.vagrant_path }
@environment = Acceptance::IsolatedEnvironment.new(apps, config.env)
# Setup a logger for this test, since tests often log to assist
# with the debugging process in case of failure.
@logger = Log4r::Logger.new("acceptance::#{self.class.name.downcase.gsub("test", "")}")
end
teardown do
@environment.close if @environment
end
end end

View File

@ -1,19 +1,21 @@
require File.expand_path("../base", __FILE__) require File.expand_path("../base", __FILE__)
class BoxTest < AcceptanceTest describe "vagrant box" do
include_context "acceptance"
def require_box(name) def require_box(name)
if !config.boxes.has_key?(name) || !File.file?(config.boxes[name]) if !config.boxes.has_key?(name) || !File.file?(config.boxes[name])
raise ArgumentError, "The configuration should specify a '#{name}' box." raise ArgumentError, "The configuration should specify a '#{name}' box."
end end
end end
should "have no boxes by default" do it "has no boxes by default" do
result = execute("vagrant", "box", "list") result = execute("vagrant", "box", "list")
assert(output(result.stdout).no_boxes, assert(output(result.stdout).no_boxes,
"output should say there are no installed boxes") "output should say there are no installed boxes")
end end
should "add a box from a file" do it "can add a box from a file" do
require_box("default") require_box("default")
# Add the box, which we expect to succeed # Add the box, which we expect to succeed
@ -26,15 +28,15 @@ class BoxTest < AcceptanceTest
"foo box should exist after it is added") "foo box should exist after it is added")
end end
should "give an error if the file doesn't exist" do it "gives an error if the file doesn't exist" do
results = execute("vagrant", "box", "add", "foo", "/tmp/nope/nope/nope/nonono.box") results = execute("vagrant", "box", "add", "foo", "/tmp/nope/nope/nope/nonono.box")
assert(!results.success?, "Box add should fail.") assert(!results.success?, "Box add should fail.")
assert(output(results.stdout).box_path_doesnt_exist, assert(output(results.stdout).box_path_doesnt_exist,
"Should show an error message about the file not existing.") "Should show an error message about the file not existing.")
end end
should "give an error if the file is not a valid box" do it "gives an error if the file is not a valid box" do
invalid = @environment.workdir.join("nope.txt") invalid = environment.workdir.join("nope.txt")
invalid.open("w+") do |f| invalid.open("w+") do |f|
f.write("INVALID!") f.write("INVALID!")
end end
@ -45,12 +47,11 @@ class BoxTest < AcceptanceTest
"should say the box is invalid") "should say the box is invalid")
end end
should "add a box from an HTTP server" do it "can add a box from an HTTP server" do
# TODO: Spin up an HTTP server to serve a file, add and test. pending("Need to setup HTTP server functionality")
skip("Need to setup HTTP server functionality")
end end
should "remove a box" do it "can remove a box" do
require_box("default") require_box("default")
# Add the box, remove the box, then verify that the box no longer # Add the box, remove the box, then verify that the box no longer
@ -63,11 +64,11 @@ class BoxTest < AcceptanceTest
"No boxes should be installed") "No boxes should be installed")
end end
should "repackage a box" do it "can repackage a box" do
require_box("default") require_box("default")
original_size = File.size(config.boxes["default"]) original_size = File.size(config.boxes["default"])
@logger.debug("Original package size: #{original_size}") logger.debug("Original package size: #{original_size}")
# Add the box, repackage it, and verify that a package.box is # Add the box, repackage it, and verify that a package.box is
# dumped of relatively similar size. # dumped of relatively similar size.
@ -75,12 +76,12 @@ class BoxTest < AcceptanceTest
execute("vagrant", "box", "repackage", "foo") execute("vagrant", "box", "repackage", "foo")
# By default, repackage should dump into package.box into the CWD # By default, repackage should dump into package.box into the CWD
repackaged_file = @environment.workdir.join("package.box") repackaged_file = environment.workdir.join("package.box")
assert(repackaged_file.file?, "package.box should exist in cwd of environment") assert(repackaged_file.file?, "package.box should exist in cwd of environment")
# Compare the sizes # Compare the sizes
repackaged_size = repackaged_file.size repackaged_size = repackaged_file.size
@logger.debug("Repackaged size: #{repackaged_size}") logger.debug("Repackaged size: #{repackaged_size}")
size_diff = (repackaged_size - original_size).abs size_diff = (repackaged_size - original_size).abs
assert(size_diff < 1000, "Sizes should be very similar") assert(size_diff < 1000, "Sizes should be very similar")
end end

View File

@ -1,8 +1,10 @@
require File.expand_path("../base", __FILE__) require File.expand_path("../base", __FILE__)
class InitTest < AcceptanceTest describe "vagrant init" do
should "create a Vagrantfile in the working directory" do include_context "acceptance"
vagrantfile = @environment.workdir.join("Vagrantfile")
it "creates a Vagrantfile in the working directory" do
vagrantfile = environment.workdir.join("Vagrantfile")
assert(!vagrantfile.exist?, "Vagrantfile shouldn't exist") assert(!vagrantfile.exist?, "Vagrantfile shouldn't exist")
result = execute("vagrant", "init") result = execute("vagrant", "init")
@ -10,8 +12,8 @@ class InitTest < AcceptanceTest
assert(vagrantfile.exist?, "Vagrantfile should exist") assert(vagrantfile.exist?, "Vagrantfile should exist")
end end
should "create a Vagrantfile with the box set to the given argument" do it "creates a Vagrantfile with the box set to the given argument" do
vagrantfile = @environment.workdir.join("Vagrantfile") vagrantfile = environment.workdir.join("Vagrantfile")
result = execute("vagrant", "init", "foo") result = execute("vagrant", "init", "foo")
assert(result.success?, "init should succeed") assert(result.success?, "init should succeed")
@ -19,8 +21,8 @@ class InitTest < AcceptanceTest
"config.vm.box should be set to 'foo'") "config.vm.box should be set to 'foo'")
end end
should "create a Vagrantfile with the box URL set to the given argument" do it "creates a Vagrantfile with the box URL set to the given argument" do
vagrantfile = @environment.workdir.join("Vagrantfile") vagrantfile = environment.workdir.join("Vagrantfile")
result = execute("vagrant", "init", "foo", "bar") result = execute("vagrant", "init", "foo", "bar")
assert(result.success?, "init should succeed") assert(result.success?, "init should succeed")

View File

@ -1,14 +1,16 @@
require File.expand_path("../base", __FILE__) require File.expand_path("../base", __FILE__)
class SSHTest < AcceptanceTest describe "vagrant ssh" do
should "fail if no Vagrantfile is found" do include_context "acceptance"
it "fails if no Vagrantfile is found" do
result = execute("vagrant", "ssh") result = execute("vagrant", "ssh")
assert(!result.success?, "vagrant ssh should fail") assert(!result.success?, "vagrant ssh should fail")
assert(output(result.stdout).no_vagrantfile, assert(output(result.stdout).no_vagrantfile,
"Vagrant should error since there is no Vagrantfile") "Vagrant should error since there is no Vagrantfile")
end end
should "fail if the virtual machine is not created" do it "fails if the virtual machine is not created" do
assert_execute("vagrant", "init") assert_execute("vagrant", "init")
result = execute("vagrant", "ssh") result = execute("vagrant", "ssh")
@ -17,7 +19,7 @@ class SSHTest < AcceptanceTest
"Vagrant should error that the VM must be created.") "Vagrant should error that the VM must be created.")
end end
should "be able to SSH into a running virtual machine" do it "is able to SSH into a running virtual machine" do
assert_execute("vagrant", "box", "add", "base", config.boxes["default"]) assert_execute("vagrant", "box", "add", "base", config.boxes["default"])
assert_execute("vagrant", "init") assert_execute("vagrant", "init")
assert_execute("vagrant", "up") assert_execute("vagrant", "up")
@ -35,7 +37,7 @@ class SSHTest < AcceptanceTest
"Vagrant should bring up a virtual machine and be able to SSH in.") "Vagrant should bring up a virtual machine and be able to SSH in.")
end end
should "be able to execute a single command via the command line" do it "is able to execute a single command via the command line" do
assert_execute("vagrant", "box", "add", "base", config.boxes["default"]) assert_execute("vagrant", "box", "add", "base", config.boxes["default"])
assert_execute("vagrant", "init") assert_execute("vagrant", "init")
assert_execute("vagrant", "up") assert_execute("vagrant", "up")

View File

@ -0,0 +1,58 @@
require File.expand_path("../isolated_environment", __FILE__)
require File.expand_path("../output", __FILE__)
require File.expand_path("../virtualbox", __FILE__)
shared_context "acceptance" do
# Setup variables for the loggers of this test. These can be used to
# create more verbose logs for tests which can be useful in the case
# that a test fails.
let(:logger_name) { "logger" }
let(:logger) { Log4r::Logger.new("acceptance::#{logger_name}") }
# This is the global configuration given by the acceptance test
# configurations.
let(:config) { $acceptance_options }
# Setup the environment so that we have an isolated area
# to run Vagrant. We do some configuration here as well in order
# to replace "vagrant" with the proper path to Vagrant as well
# as tell the isolated environment about custom environmental
# variables to pass in.
let!(:environment) do
apps = { "vagrant" => config.vagrant_path }
Acceptance::IsolatedEnvironment.new(apps, config.env)
end
before(:each) do
# Wait for VBoxSVC to disappear, since each test requires its
# own isolated VirtualBox process.
Acceptance::VirtualBox.wait_for_vboxsvc
end
after(:each) do
environment.close
end
# Executes the given command in the context of the isolated environment.
#
# @return [Object]
def execute(*args, &block)
environment.execute(*args, &block)
end
# Returns an output matcher for the given text.
#
# @return [Acceptance::Output]
def output(text)
Acceptance::Output.new(text)
end
# This method is an assertion helper for asserting that a process
# succeeds. It is a wrapper around `execute` that asserts that the
# exit status was successful.
def assert_execute(*args, &block)
result = execute(*args, &block)
assert(result.success?, "expected '#{args.join(" ")}' to succeed")
result
end
end

View File

@ -1,14 +1,14 @@
require File.expand_path("../base", __FILE__) require File.expand_path("../base", __FILE__)
class UpBasicTest < AcceptanceTest describe "vagrant up", "basics" do
should "fail if not Vagrantfile is found" do it "fails if not Vagrantfile is found" do
result = execute("vagrant", "up") result = execute("vagrant", "up")
assert(!result.success?, "vagrant up should fail") assert(!result.success?, "vagrant up should fail")
assert(output(result.stdout).no_vagrantfile, assert(output(result.stdout).no_vagrantfile,
"Vagrant should error since there is no Vagrantfile") "Vagrant should error since there is no Vagrantfile")
end end
should "bring up a running virtual machine" do it "brings up a running virtual machine" do
assert_execute("vagrant", "box", "add", "base", config.boxes["default"]) assert_execute("vagrant", "box", "add", "base", config.boxes["default"])
assert_execute("vagrant", "init") assert_execute("vagrant", "init")
assert_execute("vagrant", "up") assert_execute("vagrant", "up")

View File

@ -1,10 +1,12 @@
require File.expand_path("../base", __FILE__) require File.expand_path("../base", __FILE__)
# NOTE: Many tests in this test suite require the `expect` describe "vagrant and color output" do
# tool to be installed, because `expect` will launch with a include_context "acceptance"
# PTY.
class VagrantTestColorOutput < AcceptanceTest # This is a check to see if the `expect` program is installed on this
def has_expect? # computer. Some tests require this and if this doesn't exist then the
# test itself will be skipped.
def self.has_expect?
`which expect` `which expect`
$?.success? $?.success?
end end
@ -16,12 +18,10 @@ class VagrantTestColorOutput < AcceptanceTest
text.index("\e[31m") text.index("\e[31m")
end end
should "output color if there is a TTY" do it "outputs color if there is a TTY", :if => has_expect? do
skip("Test requires `expect`") if !has_expect? environment.workdir.join("color.exp").open("w+") do |f|
@environment.workdir.join("color.exp").open("w+") do |f|
f.puts(<<-SCRIPT) f.puts(<<-SCRIPT)
spawn #{@environment.replace_command("vagrant")} status spawn #{environment.replace_command("vagrant")} status
expect default {} expect default {}
SCRIPT SCRIPT
end end
@ -30,12 +30,10 @@ SCRIPT
assert(has_color?(result.stdout), "output should contain color") assert(has_color?(result.stdout), "output should contain color")
end end
should "not output color if there is a TTY but --no-color is present" do it "doesn't output color if there is a TTY but --no-color is present", :if => has_expect? do
skip("Test requires `expect`") if !has_expect? environment.workdir.join("color.exp").open("w+") do |f|
@environment.workdir.join("color.exp").open("w+") do |f|
f.puts(<<-SCRIPT) f.puts(<<-SCRIPT)
spawn #{@environment.replace_command("vagrant")} status --no-color spawn #{environment.replace_command("vagrant")} status --no-color
expect default {} expect default {}
SCRIPT SCRIPT
end end
@ -44,7 +42,7 @@ SCRIPT
assert(!has_color?(result.stdout), "output should not contain color") assert(!has_color?(result.stdout), "output should not contain color")
end end
should "not output color in the absense of a TTY" do it "doesn't output color in the absense of a TTY" do
# This should always output an error, which on a TTY would # This should always output an error, which on a TTY would
# output color. We check that this doesn't output color. # output color. We check that this doesn't output color.
# If `vagrant status` itself is broken, another acceptance test # If `vagrant status` itself is broken, another acceptance test

View File

@ -1,19 +1,21 @@
require File.expand_path("../base", __FILE__) require File.expand_path("../base", __FILE__)
class VersionTest < AcceptanceTest describe "vagrant version" do
should "print the version to stdout" do include_context "acceptance"
it "prints the version to stdout" do
result = execute("vagrant", "version") result = execute("vagrant", "version")
assert(output(result.stdout).is_version?(config.vagrant_version), assert(output(result.stdout).is_version?(config.vagrant_version),
"output should be version") "output should be version")
end end
should "print the version with '-v'" do it "prints the version when called with '-v'" do
result = execute("vagrant", "-v") result = execute("vagrant", "-v")
assert(output(result.stdout).is_version?(config.vagrant_version), assert(output(result.stdout).is_version?(config.vagrant_version),
"output should be version") "output should be version")
end end
should "print the version with '--version'" do it "prints the version when called with '--version'" do
result = execute("vagrant", "--version") result = execute("vagrant", "--version")
assert(output(result.stdout).is_version?(config.vagrant_version), assert(output(result.stdout).is_version?(config.vagrant_version),
"output should be version") "output should be version")

View File

@ -30,6 +30,8 @@ Gem::Specification.new do |s|
s.add_development_dependency "mocha" s.add_development_dependency "mocha"
s.add_development_dependency "posix-spawn", "~> 0.3.6" s.add_development_dependency "posix-spawn", "~> 0.3.6"
s.add_development_dependency "sys-proctable", "~> 0.9.1" s.add_development_dependency "sys-proctable", "~> 0.9.1"
s.add_development_dependency "rspec-core", "~> 2.7.1"
s.add_development_dependency "rspec-mocks", "~> 2.7.0"
s.files = `git ls-files`.split("\n") s.files = `git ls-files`.split("\n")
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact