On Nov 26, 2010, at 2:24 AM, medihack wrote:

> David, sorry for double posting (it seems I am working too much and
> forgetting about what I already asked) ... and thanks for your answer.
> How about a bit more convenient way for future releases. Something
> like:
> MyModel.stub_chain(:tag_counts, { :offset =>
> 0 }, :limit, :order).and_return([])
> it could also work for multi parameters:
> MyModel.stub_chain(:tag_counts, { :my_method => [:param1, :param2,
> param3] }, :limit, :order).and_return([])
> What do you think?

I would be opposed to this feature.

There is an old guideline in TDD that suggests that you should listen to your 
tests because when they hurt there is usually a design problem. Tests are 
clients of the code under test, and if the test hurts, then so do all of the 
other clients in the codebase. Shortcuts like this quickly become an excuse for 
poor designs. I want it to stay painful because it _should hurt_ to do this.

I understand that chains like this are common in Rails apps thanks to good 
ideas like composable finders (which generally do not violate Demeter), but I 
don't think the parallel chains should appear in client code or in specs. e.g. 
if this is a controller spec, the model should expose a single method that 
wraps this, and if it's the model spec, the spec should just call the method 
that wraps the chain with different inputs and and specify the expected 
outcomes.

Even if I were in favor of the concept, the example above is confusing because 
it is a stub that becomes a message expectation. Constrained stubs like this 
(e.g. double.stub(:method).with(:arg).and_return(value)) are confusing enough 
as it is because they don't look like message expectations, but they act sort 
of like them. I've considered deprecating "with" on stubs and pushing toward an 
inline fake implementation instead:

account.stub(:withdraw) do |*args|
  if args.first == Money.new(200, :USD)
    # do something
  else
    # do something else
  end
end

RSpec already supports this, and it makes the intent of the spec much more 
clear and localized than:

  account.stub(:withdraw).with(Money.new(200, 
:USD)).and_return(TransactionResult.new)

In this case, if account receives withdraw() with anything other than 
Money.new(200, :USD), the outcome needs to be defined elsewhere in the spec, 
and these things quickly become spread out and difficult to understand.

That's my 2ยข, but feel free to try to convince me otherwise :)

Cheers,
David
_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to