How to use "expects" in chained methods?

2,537 views
Skip to first unread message

marco

unread,
Jan 5, 2011, 10:38:56 PM1/5/11
to mocha-developer
Hello.
How do I use "expects" in chained methods, like
@users_tweets = Twitter::Search.new.from(screenname).per_page(12)

I have no idea at all.

i think thats a pretty easy thing, but I just couldn't find it in the
documentation.

regards.

James Mead

unread,
Jan 10, 2011, 6:10:22 AM1/10/11
to mocha-d...@googlegroups.com
On 6 January 2011 03:38, marco <marco...@gmail.com> wrote:
> How do I use "expects" in chained methods, like
> @users_tweets = Twitter::Search.new.from(screenname).per_page(12)

There are a few ways to approach this, depending on what you want to achieve.

If you really want to "expect" rather than "stub", here's one way you
could do it :-

search = Twitter::Search.new # or search = mock('search') if
Twitter::Search.new has any undesirable side-effects
search.expects(:from).with('floehopper').returns(search)
search.expects(:per_page).with(12).returns(search)
Twitter::Search.expects(:new).returns(search)

However, I would counsel against having these expectations setup in
more than one test. Instead, you could do something like this :-

search = stub('search')
search.stubs(:from => search, :per_page => search)
Twitter::Search.stubs(:new).returns(search)

In general, if you're likely to be calling this chain of methods from
a lot of different places in your application and/or in a lot of
tests, I'd be inclined to introduce an adapter class of some sort
which provides a simpler API specific to your application. This will
make mocking easier and tests more readable :-

If you let us know more exactly what invocations you want to "expect"
and what you want to "assert" about them, we can probably give you
more specific advice.

I hope that helps.

Regards, James.
----
http://jamesmead.org/

marco

unread,
Jan 11, 2011, 11:03:03 PM1/11/11
to mocha-developer
Hi, James!

Thanks for replying.

Well, I still don't know much about stubs and testing...

The method I am testing is a get call from a sinatra app:
get '/search' do
user_name = params["user"]
if user_name
begin
@user = Twitter.user(user_name)
@user.twits
@users_tweets = Twitter::Search.new.from(user_name).per_page(12)
rescue
@msg="User not found."
end
end
erb :index
end

I want to assert the for a given user_name, I should receive 12
tweets, and they should be displayed on the views.
Im also using rack::test for testing the responses.

What do you think?

Best regards
Marco Antonio


On Jan 10, 9:10 am, James Mead <jamesmea...@gmail.com> wrote:

James Mead

unread,
Jan 12, 2011, 10:22:41 AM1/12/11
to mocha-d...@googlegroups.com
On 12 January 2011 04:03, marco <marco...@gmail.com> wrote:
> I want to assert the for a given user_name, I should receive 12
> tweets, and they should be displayed on the views.
> Im also using rack::test for testing the responses.
>
> What do you think?

Something I hadn't noticed previously was that the instance of
Twitter::Search (presumably from John Nunemaker's Twitter gem?)
implements an "each" method and includes Enumerable. This means that
in order to render the view, you probably need to return an object
which supports the relevant methods. One simple way to do this would
be to use a simple Array of tweets. Each tweet seems to be represented
as an instance of a Hashie::Mash, so you could do something like this
:-

twitter_search = [Hashie::Mash.new(:text => "tweet one"),
Hashie::Mash.new(:text => "tweet two")]
twitter_search.stubs(:from).returns(twitter_search)
twitter_search.stubs(:per_page).returns(twitter_search)
Twitter::Search.stubs(:new).returns(twitter_search)

You may need to add other attributes to the Hashie::Mash depending on
what your view requires.

I haven't tested this, but it should allow you to avoid any external
requests to twitter.com.

If you specifically want to "assert" that Twitter::Search#from is
called with the argument "my-username", then you need to introduce a
call to Mocha::Expectation#with along the following lines :-

twitter_search.stubs(:from).with("my-username").returns(twitter_search)

If the Twitter::Search#from method is called with any other argument,
the stubbed method will return nil by default, and the test will
probably fail with a NoMethodError.

I still don't really know enough about precisely what you are trying
to test to give better advice, but hopefully this has helped. If you
want more help, can you post all the code including the ERB template,
and ideally any test code that you have already written.

James Mead

unread,
Jan 12, 2011, 10:26:31 AM1/12/11
to mocha-d...@googlegroups.com
On 12 January 2011 15:22, James Mead <james...@gmail.com> wrote:
> On 12 January 2011 04:03, marco <marco...@gmail.com> wrote:
>> I want to assert the for a given user_name, I should receive 12
>> tweets, and they should be displayed on the views.
>> Im also using rack::test for testing the responses.
>>
>> What do you think?

One last thought would be to find the actual method in the Tweetie gem
that is making the external call to twitter.com. I think it might be
the Twitter::Search#fetch method. You could then just stub this along
the following lines :-

tweets = [Hashie::Mash.new(:text => "tweet one"),


Hashie::Mash.new(:text => "tweet two")]

Twitter::Search.any_instance.stubs(:fetch).returns(tweets)

Robert Pankowecki

unread,
Jan 21, 2011, 8:42:28 AM1/21/11
to mocha-d...@googlegroups.com
I use something like this:

object.expects(:method).returns(
mock do |method|
expects(:another_method).returns(:chain_result)
end
)

object.method.another_method => :chain_result


Robert Pankowecki
http://robert.pankowecki.pl

Reply all
Reply to author
Forward
0 new messages