Posts Tagged “tests”

Update: I've refactored the code significantly and introduced the ability to explicitly unguard methods from within specs.

One problem I find myself having when writing tests is ensuring that no execution ever leaves my little test sandbox. For example, when writing tests for github-gem I keep looking through the code to find every place a function ends up `shelling out` and it's tiresome and error-prone. I finally decided I was going about this backwards. Testing is supposed to find problems for me, right? So why not raise an error whenever `` or Kernel#system() is called and let the tests tell me what else I need to mock?

Put the following code in your spec_helper.rb file and an exception will be raised whenever `` or Kernel#system() is called during a test:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class Module
  def metaclass
    class << self;self;end
  end
end
 
module Spec::Example::ExampleGroupSubclassMethods
  def add_guard(klass, name, is_class = false)
    guarded = nil # define variable now for scoping
    target = (is_class ? klass.metaclass : klass)
    sep = (is_class ? "." : "#")
    target.class_eval do
      guarded = instance_method(name)
      define_method name do |*args|
        raise "Testing guards violated: Cannot call #{klass}#{sep}#{name}"
      end
    end
    @guards ||= []
    @guards << [klass, name, is_class, guarded]
  end
 
  def add_class_guard(klass, name)
    add_guard(klass, name, true)
  end
 
  def unguard(klass, name, is_class = false)
    row = @guards.find { |(k,n,i)| k == klass and n == name and i == is_class }
    raise "#{klass}#{is_class ? '.' : '#'}#{name} is not guarded" if row.nil?
    (is_class ? klass.metaclass : klass).class_eval do
      define_method name, row.last
    end
    @guards.delete row
  end
 
  def class_unguard(klass, name)
    unguard(klass, name, true)
  end
 
  def unguard_all
    @guards ||= []
    @guards.each do |klass, name, is_class, guarded|
      (is_class ? klass.metaclass : klass).class_eval do
        define_method name, guarded
      end
    end
    @guards.clear
  end
end
 
Spec::Runner.configure do |configuration|
  configuration.prepend_before(:all) do
    self.class.send :include, Spec::Example::ExampleGroupSubclassMethods
  end
 
  configuration.prepend_before(:each) do
    add_guard Kernel, :'`'
    add_guard Kernel, :system
    add_class_guard Process, :fork
    add_class_guard Open3, :popen3 if defined? Open3
  end
 
  configuration.append_after(:each) do
    unguard_all
  end
end

Note: if you want, you can write :` instead of :'`', but my syntax highlighter here doesn't like that.

Comments Comments Off on Guarding against `โ€Œ` and Kernel#system() in your tests