Is there any way to simulate the a开发者_高级运维bsence of a gem for certain unit tests, short of actually uninstalling and then reinstalling the gem during testing?
I am writing a command line utility, and want to make sure that my tests cover cases where a user may not have all of the gems that I support. For instance, I am using fsevents — a Leopard-specific package for monitoring filesystem events — that will never be present on other systems, as well as a growl gem that's purely optional.
I used to use a method I wrote called with_constant_unavailable
. I've forgotten the exact details, but I think it was something like this:
def with_constant_unavailable(constant_name, &block)
match_data = constant_name.to_s.match(/(?:(.+)::)?(.+)/)
if match_data[2]
owning_module, constant_name = match_data[1].constantize, match_data[2]
else
owning_module, constant_name = Object, constant_name.to_s
end
original_constant = owning_module.send :remove_const, constant_name
begin
yield
ensure
owning_module.const_set constant_name, original_constant
end
end
Then you can run a test like so:
def test_uses_foo_when_bar_unavailable
with_constant_unavailable(Bar) do
assert baz.quux == Foo
end
end
You can test the existence of the constant.
if !defined?(Growl)
...
end
In your tests, you can temporary rename/disable/remove the constant to simulate the missing library.
I would try to reset the gem path, something like:
require 'rubygems'
Gem.clear_paths
ENV['GEM_HOME'] = ""
ENV['GEM_PATH'] = ""
I would also save it and restore it afterwards.
Once you've require
d something I think it's pretty difficult to undo that. What you could do, though, is have a separate set of tests that always run in a separate Ruby interpreter instance, and invoke them as a different rake
task. So you might have something like this:
Rake::TestTask.new(:no_growl_test) do |t|
t.libs << 'lib'
t.pattern = 'test/compatibility/no_growl.rb'
end
The idea being that this would run in a new, "clean" environment in which you could never load Growl. Some of the other users have suggested ways that you could make Rubygems not find the gem in question, and this might even be possible in the Gem API.
Alternatively, using JRuby it's not too difficult to create multiple disjoint run environments, but that's probably overkill for what you're doing.
精彩评论