Ok, I followed the advice of the list and moved more code into my model from my controller. When developing tests for this new code, I ran into a problem... My model code creates a receipt object and sets some values on it: @receipt = Receipt.new do |r| r.x = 1 r.y = 2 # etc end I wanted to be able to stub out Receipt.new so that I could set expectations on the methods called on the resulting Receipt object. If in my test setup I do: @receipt = mock_receipt Receipt.stub!(:new).and_return(@receipt) The block part of the code will never be called. If I do: Receipt.stub!(:new).and_yield(@receipt) Then @receipt in my model will be set to the return value of the block. I also tried something along the lines of: Receipt.stub!(:new) do yield @receipt @receipt end but got some weird no block error. Help? Am I going about this all wrong? -james -- James A. Hillyerd <james at hillyerd.com> Chief Technical Officer - ActiveRain Corp
On 4/20/07, James Hillyerd <james at hillyerd.com> wrote:> Ok, I followed the advice of the list and moved more code into my > model from my controller. When developing tests for this new code, I > ran into a problem... > > My model code creates a receipt object and sets some values on it: > > @receipt = Receipt.new do |r| > r.x = 1 > r.y = 2 > # etc > end > > I wanted to be able to stub out Receipt.new so that I could set > expectations on the methods called on the resulting Receipt object. > If in my test setup I do: > > @receipt = mock_receipt > Receipt.stub!(:new).and_return(@receipt) > > The block part of the code will never be called. If I do: > > Receipt.stub!(:new).and_yield(@receipt) > > Then @receipt in my model will be set to the return value of the block. > > I also tried something along the lines of: > > Receipt.stub!(:new) do > yield @receipt > @receipt > end > > but got some weird no block error. > > Help? Am I going about this all wrong?Any reason you don''t just query a real receipt object? describe Person do it "should create an initialized person" do Person.create_initialized("Michael").name.should == "Michael" end end class Person class << self def create_initialized(name) Person.new do |p| p.name = name end end end end What you''re trying to mock here doesn''t seem to buy you much. Unless I''m missing something.> > -james > > -- > James A. Hillyerd <james at hillyerd.com> > Chief Technical Officer - ActiveRain Corp > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
On 4/20/07, David Chelimsky <dchelimsky at gmail.com> wrote:> On 4/20/07, James Hillyerd <james at hillyerd.com> wrote: > > Ok, I followed the advice of the list and moved more code into my > > model from my controller. When developing tests for this new code, I > > ran into a problem... > > > > My model code creates a receipt object and sets some values on it: > > > > @receipt = Receipt.new do |r| > > r.x = 1 > > r.y = 2 > > # etc > > end > > > > I wanted to be able to stub out Receipt.new so that I could set > > expectations on the methods called on the resulting Receipt object. > > If in my test setup I do: > > > > @receipt = mock_receipt > > Receipt.stub!(:new).and_return(@receipt) > > > > The block part of the code will never be called. If I do: > > > > Receipt.stub!(:new).and_yield(@receipt) > > > > Then @receipt in my model will be set to the return value of the block. > > > > I also tried something along the lines of: > > > > Receipt.stub!(:new) do > > yield @receipt > > @receipt > > end > > > > but got some weird no block error. > > > > Help? Am I going about this all wrong? > > Any reason you don''t just query a real receipt object? > > describe Person do > it "should create an initialized person" do > Person.create_initialized("Michael").name.should == "Michael" > end > end > > class Person > class << self > def create_initialized(name) > Person.new do |p| > p.name = name > end > end > end > end > > What you''re trying to mock here doesn''t seem to buy you much. Unless > I''m missing something.I just tried this: irb(main):001:0> class Receipt irb(main):002:1> attr_accessor :name irb(main):003:1> end => nil irb(main):004:0> r = Receipt.new do |r| irb(main):005:1* r.name = "Ruby" irb(main):006:1> end => #<Receipt:0x82d98> irb(main):007:0> r.name => nil Does AR do something special w/ Model.new that Ruby doesn''t do already?> > > > > -james > > > > -- > > James A. Hillyerd <james at hillyerd.com> > > Chief Technical Officer - ActiveRain Corp > > _______________________________________________ > > rspec-users mailing list > > rspec-users at rubyforge.org > > http://rubyforge.org/mailman/listinfo/rspec-users > > >
I switched to using a real receipt when I couldn''t get the stubs to work, but it did make some of the tests more difficult. Plus these tests are actually on the Order object, so it seemed natural to stub out the Receipt so I''m not trying test it as well. I don''t quite follow the code that you included... are you essentially overriding the your Person model with your own class for the test? This is the snippet of code I''m trying to test. I won''t argue that I need stubs here, just that I was trying to use them and couldn''t. :) class Order < ActiveRecord::Base # ... deleted # Builds a receipt and associated receipt_items, # then distributes the payment across them. def build_receipt(date_paid, payment_method, amount_paid_cents) receipt = Receipt.new do |r| r.member = member r.payment_method = payment_method r.date_paid = date_paid end # ... deleted end end Thanks. -james On 4/20/07, David Chelimsky <dchelimsky at gmail.com> wrote:> On 4/20/07, James Hillyerd <james at hillyerd.com> wrote: > > Ok, I followed the advice of the list and moved more code into my > > model from my controller. When developing tests for this new code, I > > ran into a problem... > > > > My model code creates a receipt object and sets some values on it: > > > > @receipt = Receipt.new do |r| > > r.x = 1 > > r.y = 2 > > # etc > > end > > > > I wanted to be able to stub out Receipt.new so that I could set > > expectations on the methods called on the resulting Receipt object. > > If in my test setup I do: > > > > @receipt = mock_receipt > > Receipt.stub!(:new).and_return(@receipt) > > > > The block part of the code will never be called. If I do: > > > > Receipt.stub!(:new).and_yield(@receipt) > > > > Then @receipt in my model will be set to the return value of the block. > > > > I also tried something along the lines of: > > > > Receipt.stub!(:new) do > > yield @receipt > > @receipt > > end > > > > but got some weird no block error. > > > > Help? Am I going about this all wrong? > > Any reason you don''t just query a real receipt object? > > describe Person do > it "should create an initialized person" do > Person.create_initialized("Michael").name.should == "Michael" > end > end > > class Person > class << self > def create_initialized(name) > Person.new do |p| > p.name = name > end > end > end > end > > What you''re trying to mock here doesn''t seem to buy you much. Unless > I''m missing something. > > > > > -james > > > > -- > > James A. Hillyerd <james at hillyerd.com> > > Chief Technical Officer - ActiveRain Corp > > _______________________________________________ > > rspec-users mailing list > > rspec-users at rubyforge.org > > http://rubyforge.org/mailman/listinfo/rspec-users > > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-- James A. Hillyerd <james at hillyerd.com> Chief Technical Officer - ActiveRain Corp
On 4/20/07, James Hillyerd <james at hillyerd.com> wrote:> I switched to using a real receipt when I couldn''t get the stubs to > work, but it did make some of the tests more difficult. Plus these > tests are actually on the Order object, so it seemed natural to stub > out the Receipt so I''m not trying test it as well. > > I don''t quite follow the code that you included... are you essentially > overriding the your Person model with your own class for the test? > > This is the snippet of code I''m trying to test. I won''t argue that I > need stubs here, just that I was trying to use them and couldn''t. :) > > class Order < ActiveRecord::Base > # ... deleted > > # Builds a receipt and associated receipt_items, > # then distributes the payment across them. > def build_receipt(date_paid, payment_method, amount_paid_cents) > receipt = Receipt.new do |r| > r.member = member > r.payment_method = payment_method > r.date_paid = date_paid > end > > # ... deleted > end > endI don''t have a solution for you using mocks - but I wonder why you''re not just doing this: Receipt.new(:member => member, :payment_method => payment_method, :amount_paid_cents => amount_paid_cents) Any reason?> > Thanks. > > -james > > On 4/20/07, David Chelimsky <dchelimsky at gmail.com> wrote: > > On 4/20/07, James Hillyerd <james at hillyerd.com> wrote: > > > Ok, I followed the advice of the list and moved more code into my > > > model from my controller. When developing tests for this new code, I > > > ran into a problem... > > > > > > My model code creates a receipt object and sets some values on it: > > > > > > @receipt = Receipt.new do |r| > > > r.x = 1 > > > r.y = 2 > > > # etc > > > end > > > > > > I wanted to be able to stub out Receipt.new so that I could set > > > expectations on the methods called on the resulting Receipt object. > > > If in my test setup I do: > > > > > > @receipt = mock_receipt > > > Receipt.stub!(:new).and_return(@receipt) > > > > > > The block part of the code will never be called. If I do: > > > > > > Receipt.stub!(:new).and_yield(@receipt) > > > > > > Then @receipt in my model will be set to the return value of the block. > > > > > > I also tried something along the lines of: > > > > > > Receipt.stub!(:new) do > > > yield @receipt > > > @receipt > > > end > > > > > > but got some weird no block error. > > > > > > Help? Am I going about this all wrong? > > > > Any reason you don''t just query a real receipt object? > > > > describe Person do > > it "should create an initialized person" do > > Person.create_initialized("Michael").name.should == "Michael" > > end > > end > > > > class Person > > class << self > > def create_initialized(name) > > Person.new do |p| > > p.name = name > > end > > end > > end > > end > > > > What you''re trying to mock here doesn''t seem to buy you much. Unless > > I''m missing something. > > > > > > > > -james > > > > > > -- > > > James A. Hillyerd <james at hillyerd.com> > > > Chief Technical Officer - ActiveRain Corp > > > _______________________________________________ > > > rspec-users mailing list > > > rspec-users at rubyforge.org > > > http://rubyforge.org/mailman/listinfo/rspec-users > > > > > _______________________________________________ > > rspec-users mailing list > > rspec-users at rubyforge.org > > http://rubyforge.org/mailman/listinfo/rspec-users > > > > > -- > James A. Hillyerd <james at hillyerd.com> > Chief Technical Officer - ActiveRain Corp > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Found this in the AR source: # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with # attributes but not yet saved (pass a hash with key names matching the associated table column names). # In both instances, valid attribute keys are determined by the column names of the associated table -- # hence you can''t have attributes that aren''t part of the table columns. def initialize(attributes = nil) @attributes = attributes_from_column_definition @new_record = true ensure_proper_type self.attributes = attributes unless attributes.nil? yield self if block_given? end -james On 4/20/07, David Chelimsky <dchelimsky at gmail.com> wrote:> On 4/20/07, David Chelimsky <dchelimsky at gmail.com> wrote: > > On 4/20/07, James Hillyerd <james at hillyerd.com> wrote: > > > Ok, I followed the advice of the list and moved more code into my > > > model from my controller. When developing tests for this new code, I > > > ran into a problem... > > > > > > My model code creates a receipt object and sets some values on it: > > > > > > @receipt = Receipt.new do |r| > > > r.x = 1 > > > r.y = 2 > > > # etc > > > end > > > > > > I wanted to be able to stub out Receipt.new so that I could set > > > expectations on the methods called on the resulting Receipt object. > > > If in my test setup I do: > > > > > > @receipt = mock_receipt > > > Receipt.stub!(:new).and_return(@receipt) > > > > > > The block part of the code will never be called. If I do: > > > > > > Receipt.stub!(:new).and_yield(@receipt) > > > > > > Then @receipt in my model will be set to the return value of the block. > > > > > > I also tried something along the lines of: > > > > > > Receipt.stub!(:new) do > > > yield @receipt > > > @receipt > > > end > > > > > > but got some weird no block error. > > > > > > Help? Am I going about this all wrong? > > > > Any reason you don''t just query a real receipt object? > > > > describe Person do > > it "should create an initialized person" do > > Person.create_initialized("Michael").name.should == "Michael" > > end > > end > > > > class Person > > class << self > > def create_initialized(name) > > Person.new do |p| > > p.name = name > > end > > end > > end > > end > > > > What you''re trying to mock here doesn''t seem to buy you much. Unless > > I''m missing something. > > I just tried this: > > irb(main):001:0> class Receipt > irb(main):002:1> attr_accessor :name > irb(main):003:1> end > => nil > irb(main):004:0> r = Receipt.new do |r| > irb(main):005:1* r.name = "Ruby" > irb(main):006:1> end > => #<Receipt:0x82d98> > irb(main):007:0> r.name > => nil > > Does AR do something special w/ Model.new that Ruby doesn''t do already? > > > > > > > > > > -james > > > > > > -- > > > James A. Hillyerd <james at hillyerd.com> > > > Chief Technical Officer - ActiveRain Corp > > > _______________________________________________ > > > rspec-users mailing list > > > rspec-users at rubyforge.org > > > http://rubyforge.org/mailman/listinfo/rspec-users > > > > > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-- James A. Hillyerd <james at hillyerd.com> Chief Technical Officer - ActiveRain Corp
Nope, no real reason... was just how I wrote the code. Coming from Java, I''m used to creating an object and then calling the setters, the hashed attributes thing looks ugly to me. :) How exactly would I test that if I wanted to set an expectation on :new? Seems fragile to specify the exact Hash of attributes I''m expecting. Maybe ActiveRecord calls the :member= etc methods internally? -james> I don''t have a solution for you using mocks - but I wonder why you''re > not just doing this: > > Receipt.new(:member => member, :payment_method => payment_method, > :amount_paid_cents => amount_paid_cents) > > Any reason? > > > > > Thanks. > > > > -james > > > > On 4/20/07, David Chelimsky <dchelimsky at gmail.com> wrote: > > > On 4/20/07, James Hillyerd <james at hillyerd.com> wrote: > > > > Ok, I followed the advice of the list and moved more code into my > > > > model from my controller. When developing tests for this new code, I > > > > ran into a problem... > > > > > > > > My model code creates a receipt object and sets some values on it: > > > > > > > > @receipt = Receipt.new do |r| > > > > r.x = 1 > > > > r.y = 2 > > > > # etc > > > > end > > > > > > > > I wanted to be able to stub out Receipt.new so that I could set > > > > expectations on the methods called on the resulting Receipt object. > > > > If in my test setup I do: > > > > > > > > @receipt = mock_receipt > > > > Receipt.stub!(:new).and_return(@receipt) > > > > > > > > The block part of the code will never be called. If I do: > > > > > > > > Receipt.stub!(:new).and_yield(@receipt) > > > > > > > > Then @receipt in my model will be set to the return value of the block. > > > > > > > > I also tried something along the lines of: > > > > > > > > Receipt.stub!(:new) do > > > > yield @receipt > > > > @receipt > > > > end > > > > > > > > but got some weird no block error. > > > > > > > > Help? Am I going about this all wrong? > > > > > > Any reason you don''t just query a real receipt object? > > > > > > describe Person do > > > it "should create an initialized person" do > > > Person.create_initialized("Michael").name.should == "Michael" > > > end > > > end > > > > > > class Person > > > class << self > > > def create_initialized(name) > > > Person.new do |p| > > > p.name = name > > > end > > > end > > > end > > > end > > > > > > What you''re trying to mock here doesn''t seem to buy you much. Unless > > > I''m missing something. > > > > > > > > > > > -james > > > > > > > > -- > > > > James A. Hillyerd <james at hillyerd.com> > > > > Chief Technical Officer - ActiveRain Corp > > > > _______________________________________________ > > > > rspec-users mailing list > > > > rspec-users at rubyforge.org > > > > http://rubyforge.org/mailman/listinfo/rspec-users > > > > > > > _______________________________________________ > > > rspec-users mailing list > > > rspec-users at rubyforge.org > > > http://rubyforge.org/mailman/listinfo/rspec-users > > > > > > > > > -- > > James A. Hillyerd <james at hillyerd.com> > > Chief Technical Officer - ActiveRain Corp > > _______________________________________________ > > rspec-users mailing list > > rspec-users at rubyforge.org > > http://rubyforge.org/mailman/listinfo/rspec-users > > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-- James A. Hillyerd <james at hillyerd.com> Chief Technical Officer - ActiveRain Corp
On 4/20/07, James Hillyerd <james at hillyerd.com> wrote:> Nope, no real reason... was just how I wrote the code. Coming from > Java, I''m used to creating an object and then calling the setters, the > hashed attributes thing looks ugly to me. :) > > How exactly would I test that if I wanted to set an expectation on > :new? Seems fragile to specify the exact Hash of attributes I''m > expecting. Maybe ActiveRecord calls the :member= etc methods > internally?That''s how I do this sort of thing: Model.should_receive(:new).with(:a => "b", :c => "d") Model.new(:a => "b", :c => "d") Works fine if *your* code calls .new. Cheers, David> > -james > > > I don''t have a solution for you using mocks - but I wonder why you''re > > not just doing this: > > > > Receipt.new(:member => member, :payment_method => payment_method, > > :amount_paid_cents => amount_paid_cents) > > > > Any reason? > > > > > > > > Thanks. > > > > > > -james > > > > > > On 4/20/07, David Chelimsky <dchelimsky at gmail.com> wrote: > > > > On 4/20/07, James Hillyerd <james at hillyerd.com> wrote: > > > > > Ok, I followed the advice of the list and moved more code into my > > > > > model from my controller. When developing tests for this new code, I > > > > > ran into a problem... > > > > > > > > > > My model code creates a receipt object and sets some values on it: > > > > > > > > > > @receipt = Receipt.new do |r| > > > > > r.x = 1 > > > > > r.y = 2 > > > > > # etc > > > > > end > > > > > > > > > > I wanted to be able to stub out Receipt.new so that I could set > > > > > expectations on the methods called on the resulting Receipt object. > > > > > If in my test setup I do: > > > > > > > > > > @receipt = mock_receipt > > > > > Receipt.stub!(:new).and_return(@receipt) > > > > > > > > > > The block part of the code will never be called. If I do: > > > > > > > > > > Receipt.stub!(:new).and_yield(@receipt) > > > > > > > > > > Then @receipt in my model will be set to the return value of the block. > > > > > > > > > > I also tried something along the lines of: > > > > > > > > > > Receipt.stub!(:new) do > > > > > yield @receipt > > > > > @receipt > > > > > end > > > > > > > > > > but got some weird no block error. > > > > > > > > > > Help? Am I going about this all wrong? > > > > > > > > Any reason you don''t just query a real receipt object? > > > > > > > > describe Person do > > > > it "should create an initialized person" do > > > > Person.create_initialized("Michael").name.should == "Michael" > > > > end > > > > end > > > > > > > > class Person > > > > class << self > > > > def create_initialized(name) > > > > Person.new do |p| > > > > p.name = name > > > > end > > > > end > > > > end > > > > end > > > > > > > > What you''re trying to mock here doesn''t seem to buy you much. Unless > > > > I''m missing something. > > > > > > > > > > > > > > -james > > > > > > > > > > -- > > > > > James A. Hillyerd <james at hillyerd.com> > > > > > Chief Technical Officer - ActiveRain Corp > > > > > _______________________________________________ > > > > > rspec-users mailing list > > > > > rspec-users at rubyforge.org > > > > > http://rubyforge.org/mailman/listinfo/rspec-users > > > > > > > > > _______________________________________________ > > > > rspec-users mailing list > > > > rspec-users at rubyforge.org > > > > http://rubyforge.org/mailman/listinfo/rspec-users > > > > > > > > > > > > > -- > > > James A. Hillyerd <james at hillyerd.com> > > > Chief Technical Officer - ActiveRain Corp > > > _______________________________________________ > > > rspec-users mailing list > > > rspec-users at rubyforge.org > > > http://rubyforge.org/mailman/listinfo/rspec-users > > > > > _______________________________________________ > > rspec-users mailing list > > rspec-users at rubyforge.org > > http://rubyforge.org/mailman/listinfo/rspec-users > > > > > -- > James A. Hillyerd <james at hillyerd.com> > Chief Technical Officer - ActiveRain Corp > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >