Guarding against `โ` and Kernel#system() in your tests
Posted by: Kevin Ballard in Programming, Ruby, tags: RSpec, Ruby, testsUpdate: 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.
Entries (RSS)