开发者

Simulating a missing gem in Ruby unit tests

开发者 https://www.devze.com 2022-12-28 02:24 出处:网络
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?

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 required 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消