thoughtbot/shoulda-context

Dynamically created test methods lose original test's source_location

Closed this issue · 3 comments

This is causing problems downstream in ArturT/knapsack where we are expecting the source_location of the method to point to a test file, but with shoulda-context installed links to lib/shoudla/context/context.rb line 399, where we are dynamically defining methods. I'm looking into a way to fix this, but just wanted to raise the issue to the community at large.

This issue can be closed now - it was fixed in #51 and released in v1.2.2

Seems that it's still happening for cases like

context "validations"
  should validate_presence...
end

A dirty fix could be the below. But obviously something more reliable is needed.

diff --git a/lib/shoulda/context/context.rb b/lib/shoulda/context/context.rb
index cff6860..dcdd04e 100644
--- a/lib/shoulda/context/context.rb
+++ b/lib/shoulda/context/context.rb
@@ -341,15 +341,18 @@ module Shoulda
       end
 
       def should(name_or_matcher, options = {}, &blk)
+        source = nil
         if name_or_matcher.respond_to?(:description) && name_or_matcher.respond_to?(:matches?)
           name = name_or_matcher.description
+          source = caller.grep(/#{test_unit_class.name}/).first.split(":")[0, 2]
+          source[1] = source[1].to_i
           blk = lambda { assert_accepts name_or_matcher, subject }
         else
           name = name_or_matcher
         end
 
         if blk
-          self.shoulds << { :name => name, :before => options[:before], :block => blk }
+          self.shoulds << { :name => name, :before => options[:before], :block => blk, :source => source }
         else
           self.should_eventuallys << { :name => name }
         end
@@ -357,8 +360,10 @@ module Shoulda
 
       def should_not(matcher)
         name = matcher.description
+          source = caller.grep(/#{test_unit_class.name}/).first.split(":")[0, 2]
+          source[1] = source[1].to_i
         blk = lambda { assert_rejects matcher, subject }
-        self.shoulds << { :name => "not #{name}", :block => blk }
+        self.shoulds << { :name => "not #{name}", :block => blk, :source => source }
       end
 
       def should_eventually(name, &blk)
@@ -396,7 +401,7 @@ module Shoulda
         end
 
         test_methods[test_unit_class][test_name.to_s] = true
-        file, line_no = should[:block].source_location
+        file, line_no = should[:source] || should[:block].source_location
         context = self
         test_unit_class.class_eval <<-end_eval, file, line_no
           define_method test_name do

Hi @cristianbica, can you make a new issue for this and provide a reproducible case? I'm having trouble understanding why this fix is needed.