Need help mocking this out

Let’s say you’re using the restful_authentication plugin.
You have a model called articles. On the index action of the
articlescontroller you simply want to spec out that it’ll scope the
results
to the ownership of the current_user.

It should NOT include any articles other than the articles that user
owns.

How would you properly spec this out?

Thanks for the help!

Typically, I’d write a method in your user model that returns the user’s
articles:

class User do

def find_articles_for_user
Article.find(:all, :conditions => [‘userid = ?’, id)
end

end

Then you’d use a mock in your controller spec, and make sure you test
that
your method is being called.

On the other hand, the user model should be tested directly against the
db.

HTH,

Stefan

2007/12/3, Fischer, Daniel [email protected]:

yuck, that seems kind of nasty, no?
user.articles is already scoped…

There has to be a different solution!

On Dec 3, 2007 2:07 AM, Stefan Magnus Landrø [email protected]

On 3.12.2007, at 11.57, Fischer, Daniel wrote:

Let’s say you’re using the restful_authentication plugin.

You have a model called articles. On the index action of the
articlescontroller you simply want to spec out that it’ll scope the
results to the ownership of the current_user.

It should NOT include any articles other than the articles that user
owns.

Given that you have

user has_many :articles

You want to use current_user.articles (or
current_user.articles.find(additional options)).

In the spec, you want to make sure that the articles are scoped
through the correct user:

before(:each) do
@user = mock_model(User)
User.stub!(:find).and_return(@user) # to make sure current_user is
the mock model
@articles = [mock_model(Article)]
end

it “should find articles for the current user” do
@user.should_receive(:articles).and_return(@articles)
get :index
end

It is then the responsibility of the articles association to scope the
results. In the controller specs you just trust that it works.


Jarkko L.

http://www.railsecommerce.com
http://odesign.fi

Hi Daniel,

You’re trying to do too much in the controller. It’s not the
controller’s responsibility to ensure that the user is capable of
returning its own article without including anyone else’s - that’s
the user’s (or the Article model’s, perhaps) responsibility. Your
controller should be thin and simple, delegating all business logic
onto the model objects. See this article: http://www.pragprog.com/
articles/tell-dont-ask

Once thinned down like this, speccing the controller becomes trivial

  • just mock the current_user method and ensure that it receives a
    call to “available_articles” or whatever the method name is. Then,
    spec that method in the User model. At that point, don’t mock
    anything you don’t have to, and test the behaviour of the User model
    (e.g. “it should not return anyone else’s article within its
    available articles” or something like that).

Hope this helps,

Daniel

Assuming that there is a call like this in your controller
@articles = current_user.articles

One way to do this is to stub out the controller.current_user to return
a
mock object of the current_user

Then put an expectation on the current user that it’s articles method
gets
called. (return a mocked collection of articles)

Then check that @articles is set to the returned mocked collection of
articles from current_user.articles

phew…

Ok So one way you might write this could be (This is untested…)

it “should scope the articles to the currrent_user” do

user = mock_model(User)
articles = [mock_model(Article)]

controller.stub!(:current_user).and_return(user)
user.should_receive(:articles).and_return(articles)

get :index

assigns[:articles].should == articles

end

Like I said though, that’s not tested itself. If that’s not exactly
right… it’s along the right track of an option that can work.

HTH
Daniel

On Dec 3, 2007 9:07 PM, Stefan Magnus Landrø [email protected]

Hey cool, thanks for the help guys. One problem though, when I take this
approach I can’t decouple the specs anymore. They all “User_xxx receive
unexpected message :articles”. It seems silly to include all behaviors
in
one spec, or put that expectation in each test. Is there a way around
this?
Thanks for all the help,
Daniel F.
http://www.danielfischer.com

Well, if articles is already scoped, you just say:

user.should_receive(‘articles’).and_return(some_articles)

To make sure your articles method is scoped, write a model test that
hits
the database, and verify that only the user’s articles are returned.

As Daniel T. says, you should try to keep your controllers as thin
as
possible. In addition you should try to keep your tests as fast as
possible

  • and you typically do that by not hitting the DB.

Stefan

2007/12/3, Fischer, Daniel [email protected]:

I also have another problem, when I am trying to do the similar strategy
for
XML based speccing it fails on saying nil.to_xml
Arg…

And all it is, is the following => (with a before_filter on login
required)
but still, it’s just current_user.

def show
@writing = current_user.writings.find(params[:id])

respond_to do |format|
  format.html
  format.xml { render :xml => @writing.to_xml }
end

end

http://pastie.textmate.org/private/99vq9ipqb6u8cu5bfirlaa here is the
pastie.

Alright, thanks, I’m getting more progress in this. As soon as I figure
this
out I won’t have too many problems… hopefully.
Anyone know what “undefined method `call’ for “1”:String” means?

I’m trying to do this:

http://pastie.textmate.org/private/17jjjmbave0ph2mkcgp6w

On Dec 3, 2007 10:26 PM, Fischer, Daniel [email protected] wrote:

Hey cool, thanks for the help guys. One problem though, when I take this
approach I can’t decouple the specs anymore. They all “User_xxx receive
unexpected message :articles”. It seems silly to include all behaviors in
one spec, or put that expectation in each test. Is there a way around this?

Sure. Create a method somewhere that generates a baseline stub for you.

module UserControllerSpecHelper
def create_stub_user
mock_model(User, :articles => [])
end
end

describe UserController, “…” do
include UserControllerSpecHelper
before(:each) do
@user = create_stub_user
end
end

If there are other methods you need to stub on all instances of user,
do it in the helper. Then you can use message expectations (mocks)
where you need them to describe specific behaviour.

HTH,
David

Sorry for so many messages, I hope I don’t get in trouble for this.
Maybe
IRC would be better if there was a RSpec one.
Anyway, the previous problem was solved with the following
http://pastie.textmate.org/private/m6qqfd7tzeanw2yar8rua

The problem was caused by :

@user = mock_model(User, :writings => [] )

I’m not sure if that is a bug or what, but that’s what caused it.

It’d also say “no block given” if I put something there other than a
“mock”.

On 4.12.2007, at 8.40, Fischer, Daniel wrote:

Sorry for so many messages, I hope I don’t get in trouble for this.
Maybe IRC would be better if there was a RSpec one.

rspec @freenode

//jarkko


Jarkko L.

http://www.railsecommerce.com
http://odesign.fi

btw, to answer the same question for member actions (such as show),
you can stub or assert the association proxy class

@article = current_user.articles.find(params[:id])

then,

it “should find article scoped by current_user” do
article = mock_model(Article)
current_user.should_receive(:articles).and_return(Article)
Article.should_receive(:find).and_return(article)
get :show, :id => article.id
end

this will ensure that your controller is not calling Article.find
unscoped

linoj

On Dec 17, 2007 8:43 AM, David C. [email protected] wrote:

I’m also usually writing the examples from the code first. So I’d do
something like:

it “should find the current users articles” do
article = mock_model(Article)
current_user.should_receive(:find_article).with(“1”).and_return(Article)
get :show, :id => “1”
end

Where is current_user defined here? I’ve always stubbed current_user on
the
controller object. How are you doing this one?

On Dec 16, 2007 11:03 AM, Jonathan L. [email protected]
wrote:

current_user.should_receive(:articles).and_return(Article)
Article.should_receive(:find).and_return(article)
get :show, :id => article.id
end

this will ensure that your controller is not calling Article.find unscoped

I tend to take this one more step. I’m a bit of a demeter zealot, and
I’m also usually writing the examples from the code first. So I’d do
something like:

it “should find the current users articles” do
article = mock_model(Article)
current_user.should_receive(:find_article).with(“1”).and_return(Article)
get :show, :id => “1”
end

This would lead me to add a find_article method to User:

describe User do
it “should find it’s own articles” do
user = User.new
user.articles.should_receive(:find).with(“1”)
user.find_article("1)
end
end

This has two benefits:

  • each example is simpler and focuses on one object and how it behaves
  • I can change the relationship between a User and it’s Articles
    without changing the controller code.

FWIW,
David

On Dec 16, 2007 1:43 PM, David C. [email protected] wrote:

I tend to take this one more step. I’m a bit of a demeter zealot, and
I’m also usually writing the examples from the code first. So I’d do
something like:

it “should find the current users articles” do
article = mock_model(Article)
current_user.should_receive(:find_article).with(“1”).and_return(Article)
get :show, :id => “1”
end

I find that there are two problems with this approach:

  • since proxies provide so much behavior, wrapping the proxy’s methods
    like this can quickly lead to a bloated API on User
  • The Rails Way seems to care a lot less about demeter than you do.
    Even though the method name is obvious, you still get people going,
    “huh? why doesn’t he just do user.articles.find?”

I’m right there with you in theory, but in practice I’ve found that
people simply don’t like it. So I’ve just had to bite my tongue and
write slightly uglier specs (I won’t give in on add_article, however.
articles.should_receive(:<<) gives me sharp stomach pains)

It has provided me a nice opportunity to express intent, though.
Whenever I want a default finder that does slightly more than
articles.find(1), I can write a find_article method to encapsulate it.
Then when someone asks me, “well why don’t use you juse
articles.find” I can reply that there’s a little more than meets the
eye. But how great is it that they don’t have to know what it is in
order to use it! Maybe there’s something to this approach after all.

So like I said, in general I prefer your approach, but I’ve opted for
the principle of least surprise with my coworkers. I think I make the
best of it by communicating those times when I depart from the
standard Rails chaining style.

Pat

On Dec 16, 2007 4:01 PM, Daniel N [email protected] wrote:

stub or assert the association proxy class
end
current_user.should_receive(:find_article).with(“1”).and_return(Article)
get :show, :id => “1”
end

Where is current_user defined here? I’ve always stubbed current_user on the
controller object. How are you doing this one?

Well that was just an example, not taken from any real code. You could
either get it from the controller in the example:

controller.current_user.should_receive(:find_article)

or create a helper method to get at it.