Re: [rspec] rspec2 throws 'undefined method' for model class method (.self)

290 views
Skip to first unread message

David Chelimsky

unread,
Dec 5, 2012, 4:32:41 PM12/5/12
to rs...@googlegroups.com
On Wed, Dec 5, 2012 at 3:23 PM, netzfisch <tj2...@gmail.com> wrote:
> I have model (event.rb) method, that retrieves a list of all recurrence
> dates for the named period
>
> def self.dates_between(start_date, end_date)
> dates = (start_date..end_date).step(7).to_a
> end
>
> than I specify the following in event_spec.rb
>
> before(:each) do
> @event = FactoryGirl.create(:event)
> end
> subject { @event }

Side note: recommend using this instead:

subject(:event) { FactoryGirl.create(:event) }

>
> ... other working tests ...
>
> describe '#dates_between' do
> context 'finds recurrences dates of a event' do
> start_date = "2012-12-01 18:25:25"
> end_date = "2012-12-15 18:25:25"
> output_dates = ["2012-12-01 18:25:25", "2012-12-08 18:25:25",
> "2012-12-15 18:25:25"]
>
> it 'should call Event with method dates_between' do
> @event.should_receive(:dates_between).with(start_date, end_date)
> @event.dates_between(start_date, end_date)
> end

This ^^ will pass regardless of whether the method is defined in the
right place because the should_receive statement defines the
dates_between method if it's not there. In other words, this example
is not actually testing anything besides how rspec-mocks works.

>
> it 'should find and return the RIGHT recurrences dates' do
> @event.dates_between(start_date, end_date).should
> eq(output_dates)

This is probably the one that's failing because you're defining a
class method but you're calling an instance method.

> end
> end
> end
>
> and get this failure:
>
> 1) Event#dates_between finds recurrences dates of a event should find
> and return the RIGHT recurrences dates
> Failure/Error: @event.dates_between(start_date, end_date).should
> eq(output_dates)
> NoMethodError:
> undefined method `dates_between' for #<Event:0xb99e9f8>
> # ./spec/models/event_spec.rb:52:in `block (4 levels) in <top
> (required)>'
>
> when I change in the model from a class method to an instance method
> (removing "self.") the console just prints out "wild data":
>
>> 22:93:55", "2012-12-01 22:93:62", "2012-12-01 22:93:69", "2012-12-01
>> 22:93:76", "2012-12-01 22:93:83", "2012-12-01 22:93:90", "2012-12-01
>> 22:93:97", "2012-12-01 22:94:04", "2012-12-01 22:94:11", "2012-12-01
>> 22:94:18", "2012-12-01 22:94:25", "2012-12-01 22:94:32", ...

What is wild about that data? Looks like what you're defining in the
example. What data are you expecting here?

netzfisch

unread,
Dec 6, 2012, 8:42:45 AM12/6/12
to rs...@googlegroups.com
Am Mittwoch, 5. Dezember 2012 22:32:41 UTC+1 schrieb dchel...@gmail.com:
On Wed, Dec 5, 2012 at 3:23 PM, netzfisch <tj2...@gmail.com> wrote:
> I have model (event.rb) method, that retrieves a list of all recurrence
> dates for the named period
>
>       def self.dates_between(start_date, end_date)
>          dates = (start_date..end_date).step(7).to_a
>       end
>
> than I specify the following in event_spec.rb
>
>       before(:each) do
>         @event = FactoryGirl.create(:event)
>       end
>       subject { @event }

Side note: recommend using this instead:

  subject(:event) { FactoryGirl.create(:event) }
 

thanks for the feedback: ist that statement also executed before every describe/it statement, because there are further test: see https://github.com/netzfisch/teamorga/blob/master/spec/models/event_spec.rb 


>
>       ... other working tests ...
>
>       describe '#dates_between' do
>         context 'finds recurrences dates of a event' do
>           start_date = "2012-12-01 18:25:25"
>           end_date = "2012-12-15 18:25:25"
>           output_dates = ["2012-12-01 18:25:25", "2012-12-08 18:25:25",
> "2012-12-15 18:25:25"]
>
>           it 'should call Event with method dates_between' do
>             @event.should_receive(:dates_between).with(start_date, end_date)
>             @event.dates_between(start_date, end_date)
>           end

This ^^ will pass regardless of whether the method is defined in the
right place because the should_receive statement defines the
dates_between method if it's not there. In other words, this example
is not actually testing anything besides how rspec-mocks works.

ok, the intention was, to test that the class method get called (I read that this the main difference between 'stub' and 'should_receive'  with the two attributes? How should I change that? 
 

>
>           it 'should find and return the RIGHT recurrences dates' do
>             @event.dates_between(start_date, end_date).should
> eq(output_dates)

This is probably the one that's failing because you're defining a
class method but you're calling an instance method.

right, but how do I ask for a class-method. Beside that I could change the behaviour in the modell and controller, but that is not really intended: https://github.com/netzfisch/teamorga/blob/master/app/models/event.rb
https://github.com/netzfisch/teamorga/blob/master/app/controllers/events_controller.rb


>           end
>         end
>       end
>
> and get this failure:
>
>     1) Event#dates_between finds recurrences dates of a event should find
> and return the RIGHT recurrences dates
>      Failure/Error: @event.dates_between(start_date, end_date).should
> eq(output_dates)
>      NoMethodError:
>        undefined method `dates_between' for #<Event:0xb99e9f8>
>      # ./spec/models/event_spec.rb:52:in `block (4 levels) in <top
> (required)>'
>
> when I change in the model from a class method to an instance method
> (removing "self.") the console just prints out "wild data":
>
>> 22:93:55", "2012-12-01 22:93:62", "2012-12-01 22:93:69", "2012-12-01
>> 22:93:76", "2012-12-01 22:93:83", "2012-12-01 22:93:90", "2012-12-01
>> 22:93:97", "2012-12-01 22:94:04", "2012-12-01 22:94:11", "2012-12-01
>> 22:94:18", "2012-12-01 22:94:25", "2012-12-01 22:94:32", ...

What is wild about that data? Looks like what you're defining in the
example. What data are you expecting here?

ok, you are right, I expect

  ["2012-12-01 18:25:25", "2012-12-08 18:25:25", "2012-12-15 18:25:25"] 

but probably should expext without time, which screws my console by iterating through every second - minute - hour for the three expected days

  ["2012-12-01", "2012-12-08", "2012-12-15"] 

although it works in production? Probably I should define in the model a date-format without time - not sure there is one? Anyway, it is not really intended:

Myron Marston

unread,
Dec 6, 2012, 3:32:34 PM12/6/12
to rs...@googlegroups.com


On Wednesday, December 5, 2012 1:23:36 PM UTC-8, netzfisch wrote:
I have model (event.rb) method, that retrieves a list of all recurrence dates for the named period

      def self.dates_between(start_date, end_date)
         dates = (start_date..end_date).step(7).to_a
      end

than I specify the following in event_spec.rb

      before(:each) do
        @event = FactoryGirl.create(:event)
      end    
      subject { @event }

      ... other working tests ...
    
      describe '#dates_between' do
        context 'finds recurrences dates of a event' do
          start_date = "2012-12-01 18:25:25"
          end_date = "2012-12-15 18:25:25"
          output_dates = ["2012-12-01 18:25:25", "2012-12-08 18:25:25", "2012-12-15 18:25:25"]
    
          it 'should call Event with method dates_between' do
            @event.should_receive(:dates_between).with(start_date, end_date)
            @event.dates_between(start_date, end_date)
          end
    
          it 'should find and return the RIGHT recurrences dates' do
            @event.dates_between(start_date, end_date).should eq(output_dates)
          end
        end
      end

and get this failure:

    1) Event#dates_between finds recurrences dates of a event should find and return the RIGHT recurrences dates
     Failure/Error: @event.dates_between(start_date, end_date).should eq(output_dates)
     NoMethodError:
       undefined method `dates_between' for #<Event:0xb99e9f8>
     # ./spec/models/event_spec.rb:52:in `block (4 levels) in <top (required)>'

when I change in the model from a class method to an instance method (removing "self.") the console just prints out "wild data":

> 22:93:55", "2012-12-01 22:93:62", "2012-12-01 22:93:69", "2012-12-01
> 22:93:76", "2012-12-01 22:93:83", "2012-12-01 22:93:90", "2012-12-01
> 22:93:97", "2012-12-01 22:94:04", "2012-12-01 22:94:11", "2012-12-01
> 22:94:18", "2012-12-01 22:94:25", "2012-12-01 22:94:32", ...

any ideas?

If you want to test a class method, then your test needs to send the message *to the class object*, not to an instance of that class:

class Event
  def self.dates_between
  end
end

Event.dates_between # calls the defined method
Event.new.dates_between # raises a NoMethodError because Event instances don't respond to #dates_between

HTH,
Myron 

netzfisch

unread,
Dec 6, 2012, 5:03:54 PM12/6/12
to rs...@googlegroups.com
Thanks, both answers helped me getting the specs green!
Last questions, 
  • does the first "it-statement" make sense now? 
  • Will the "subject-statement" also executed before(:each) describe-/it-statement?
Now I do:

class Event
  def self.dates_between(start_date, end_date)
    dates = (start_date..end_date).step(7).to_a
  end
end

describe Event do

  subject(:event) { FactoryGirl.create(:event) }

  describe '#dates_between' do
    context 'finds recurrences dates of a event' do
 
      start_date = "2012-12-01"
      end_date = "2012-12-15"
      output_dates = ["2012-12-01", "2012-12-08", "2012-12-15"]

      it 'should call dates_between with two arguments' do
        event.should_receive(:dates_between).with(start_date, end_date).and_return(output_dates)
        event.dates_between(start_date, end_date).should eq(output_dates)
      end

      it 'should find and return the RIGHT recurrences dates' do
        Event.dates_between(start_date, end_date).should eq(output_dates)
      end
    end
  end
end
Reply all
Reply to author
Forward
0 new messages