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

View File

@ -1,11 +1,11 @@
require "rubygems"
require "contest"
require "rspec/autorun"
require "log4r"
require File.expand_path("../helpers/config", __FILE__)
require File.expand_path("../helpers/isolated_environment", __FILE__)
require File.expand_path("../helpers/output", __FILE__)
require File.expand_path("../helpers/virtualbox", __FILE__)
require File.expand_path("../support/base_context", __FILE__)
require File.expand_path("../support/config", __FILE__)
require File.expand_path("../support/virtualbox", __FILE__)
# If VirtualBox is currently running, fail.
if Acceptance::VirtualBox.find_vboxsvc
@ -32,55 +32,7 @@ end
$acceptance_options = Acceptance::Config.new(ENV["ACCEPTANCE_CONFIG"])
class AcceptanceTest < Test::Unit::TestCase
# This method is a shortcut to give access to the global configuration
# setup by the acceptance tests.
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
# Configure RSpec
RSpec.configure do |c|
c.expect_with :stdlib
end

View File

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

View File

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

View File

@ -1,14 +1,16 @@
require File.expand_path("../base", __FILE__)
class SSHTest < AcceptanceTest
should "fail if no Vagrantfile is found" do
describe "vagrant ssh" do
include_context "acceptance"
it "fails if no Vagrantfile is found" do
result = execute("vagrant", "ssh")
assert(!result.success?, "vagrant ssh should fail")
assert(output(result.stdout).no_vagrantfile,
"Vagrant should error since there is no Vagrantfile")
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")
result = execute("vagrant", "ssh")
@ -17,7 +19,7 @@ class SSHTest < AcceptanceTest
"Vagrant should error that the VM must be created.")
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", "init")
assert_execute("vagrant", "up")
@ -35,7 +37,7 @@ class SSHTest < AcceptanceTest
"Vagrant should bring up a virtual machine and be able to SSH in.")
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", "init")
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__)
class UpBasicTest < AcceptanceTest
should "fail if not Vagrantfile is found" do
describe "vagrant up", "basics" do
it "fails if not Vagrantfile is found" do
result = execute("vagrant", "up")
assert(!result.success?, "vagrant up should fail")
assert(output(result.stdout).no_vagrantfile,
"Vagrant should error since there is no Vagrantfile")
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", "init")
assert_execute("vagrant", "up")

View File

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

View File

@ -1,19 +1,21 @@
require File.expand_path("../base", __FILE__)
class VersionTest < AcceptanceTest
should "print the version to stdout" do
describe "vagrant version" do
include_context "acceptance"
it "prints the version to stdout" do
result = execute("vagrant", "version")
assert(output(result.stdout).is_version?(config.vagrant_version),
"output should be version")
end
should "print the version with '-v'" do
it "prints the version when called with '-v'" do
result = execute("vagrant", "-v")
assert(output(result.stdout).is_version?(config.vagrant_version),
"output should be version")
end
should "print the version with '--version'" do
it "prints the version when called with '--version'" do
result = execute("vagrant", "--version")
assert(output(result.stdout).is_version?(config.vagrant_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 "posix-spawn", "~> 0.3.6"
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.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact