DiyMocksVMocha

June 13, 2012
See DiyMocks

An example of a less than helpful mocha error message in Rails.

not all expectations were satisfied
unsatisfied expectations:
- expected exactly once, not yet invoked: Purchase(id: integer,
person_id: integer, deal_id: integer, created_at: datetime,
updated_at: datetime, credit_card_id: integer, ref_code: string,
charged_at: datetime, coupons_count: integer, aasm_state: string,
approval_code: string, referring_purchase_id: integer,
referring_purchases_count: integer, discount: decimal,
referring_user_id: integer, last_transaction_id: string, collapsed:
boolean, blasted_to_feed_at: datetime, fb_fan_incentive: decimal,
deleted_at: datetime, latitude: float, longitude: float, ip_address:
string, visa_discount: decimal, purchaser_id: integer,
cheerios_discount: decimal, city_id: integer).find('1234567')




stub...

purchase = stub()
purchase.stubs(:id).returns(123)
purchase.stubs(:quantity).returns(2)

deal = stub()
deal.stubs(:title).returns("Mr. Bones's Deal'")
deal.stubs(:price).returns(12)
deal.stubs(:currency_unit).returns('$')

person = stub()
person.stubs(:email).returns('chris.morris@livingsocial.com')
person.stubs(:id).returns(8675309)

purchase.stubs(:person).returns(person)
purchase.stubs(:deal).returns(deal)


or Struct?

PurchaseStub = Struct.new(:id, :quantity, :deal, :person)
DealStub = Struct.new(:title, :price, :currency_unit)
PersonStub = Struct.new(:id, :email)

person = PersonStub.new(8675309, 'chrismo@ls.com')
deal = DealStub.new("Mr. Bones's Deal'", 12, '$')
purchase = PurchaseStub.new(123, 2, deal, person)


In the Struct case, it's easier to start with the default setup, and then override it once in the one test that needs it overwritten. The stubs example is more verbose but is clearer to read (and edit) since the field is right there with the value - though still more verbose than a simple assignment.

--

“I don't like the expects error output - sometimes it has too much data in it too wade through.”
“Then just use stubs.”
“But what about when I need to assert expectations?”
[what does that look like?]
- or -
“Just use OpenStruct”

--

OpenStruct is working nicely. “But then why not stubs?” OpenStruct is right there - and it's a built-in Ruby syntax - not a DSL and not another external dependency.

--

Aha! When my class under test calls a static method on another class and I just want to mock that out - mocha is great. ExternalClass.any_instance...

I'll give you mocha is easier there - but you're enabling a Dependency Inversion violation (SOLID). Sometimes you just have to work with what you've got - but, better to refactor the dependency so you can easily manually mock it. Makes the class under test better.

--

One nitpick on stubbing - I don't need this dump of output:


unexpected invocation: #<Mock:0x1022aff50>.reload()
satisfied expectations:
- allowed any number of times, not yet invoked: #<Mock:0x1022b6df0>.available_credit(any_parameters)
- allowed any number of times, not yet invoked: #<Mock:0x1022b6df0>.available_credit(any_parameters)
- allowed any number of times, not yet invoked: #<Mock:0x1022b6df0>.Person(any_parameters)
- allowed any number of times, not yet invoked: #<Mock:0x1022b57e8>.LocalDeal(any_parameters)
- allowed any number of times, not yet invoked: #<Mock:0x1022aff50>.Purchase(any_parameters)
- allowed any number of times, not yet invoked: #<Mock:0x1022aff50>.total_price(any_parameters)
- allowed any number of times, not yet invoked: #<Mock:0x1022aff50>.alternate_payment(any_parameters)
- allowed any number of times, invoked once: #<Mock:0x1022aff50>.person(any_parameters)
- allowed any number of times, invoked once: #<Mock:0x1022aff50>.deal(any_parameters)
- allowed any number of times, not yet invoked: #<Mock:0x1022aff50>.aasm_state(any_parameters)
- allowed any number of times, not yet invoked: #<Mock:0x1022aff50>.id(any_parameters)
- allowed any number of times, invoked once: #<Mock:0x1022aff50>.referred_purchases(any_parameters)


--

and with OpenStruct I can easily modify the value by setting it - without calling stubs again - or have the production code set it and retain that value.

tags: ComputersAndTechnology