[rspec-users] Nosy controller specs

Andrew Premdas apremdas at gmail.com
Thu Dec 11 15:02:54 EST 2008


Thanks for reply. The one year ago thing was a really bad example. Using
your terminilogy  it looks like a rule that changes what my app does. But I
was talking about a change in what the app is; i.e. a business rule.

I definitely don't want to have business rules in controllers. From my
viewpoint in MVC the controller is a conduit that allows the business domain
encapsulated by the model to be presented in views and in reverse allows
interaction with views to communicate with the model. In Rails though its
very easy for the business domain to creep into the controller. I'm
beggining to think that Rails got this wrong. Perhaps it should have
specified a restrictive api between the model and the controller, I've been
thinking of something like


By giving completely open access to anything in the model including all
classes its real easy for controllers to get real messy. I think the
emergence of Presenters as well as skinny controllers is symptomatic of that

With a resource based approach the controller really should only be
requesting resources or more accurately representations of resources. It
shouldn't be creating/specifying representations of resources. That is
outside the responsibility of a controller. Again using your terminology
REST restricts what an application does by using a standard set of verbs to
do things. Now in Rails we can pollute this by putting all sorts of stuff in
our restful methods, but really we shouldn't. Instead we should use the
expressivness of what 'is' to get our functionality. If we do this then our
controllers can be ultra skinny and our controller specs should be much less
brittle. To change what our app 'does' we then add new routes, controllers
and views to show a different representation from our model but still using
our standard verbs.

If controllers only use the restrictive api I've suggested and we combine
this with a 'presenter' layer (when required) on top of our model. then
controller specs really can be very straightforward and focus on their key
responsibilities which are getting something and controlling i.e. routes.

I'll try and give a better example. In my business domain I have products
and some of these products have refills which are also products. Generally
when I want to look at all products I don't want to see the refills. This is
because the refills are bought and specified through their product. So using
tags when I want to find 'all' products I'll actually do the following
bizzarre thing

   Product.find_tagged_with('refill', :exclude => 'refill')

Now generally in Rails this will be done in my controllers index method, and
with rspecs scaffold code we'd mock Product and test that this method was
called the correct way. So as my call has changed from Product.find(:all) to
this new call I have to change my controller spec. Now changing what all
products means has nothing to do with controllers and everything to do with
models. What my controller should have done is called
Product.get_collection. Then when I change my definition of what
get_collection does then yes I have to update my model spec, but that makes
sense, and my controller and its specs remain the same.

I don't see controller specs as being fixed, but I do think they should only
need to change when something a controller is responsible for changes.
Generally this is routing - controlling where we go - which is fundamentally
how web applications do things.

Anyhow hope that makes sense and once again thanks for the input much


2008/12/11 Pat Maddox <pergesu at gmail.com>

> "Mark Wilden" <mark at mwilden.com> writes:
> > On Thu, Dec 11, 2008 at 4:33 AM, Andrew Premdas <apremdas at gmail.com>
> wrote:
> >
> >             it "should find all posts" do
> >               Post.should_receive(:find).with(:all).and_return([@post])
> >               do_get
> >             end
> >
> >         Now this last spec "should find all posts" is nosy is far as I'm
> concerned in that its specifying how the model should get all the
> >         posts (i.e. white box testing) rather than checking the result
> i.e. that its got @post back. So now if I change the model so that
> >         "all posts" is for example "all posts in last year" because there
> is a new business rule that we don't show posts over a year old
> >         then my controller spec fails.
> >
> > I think this is probably correct as is.
> >
> > When specing a controller, it's correct BDD to specify how it interacts
> with other objects. If the controller itself wants all posts in the
> > last year, it's OK to test that. The fact that only this year's posts are
> of interest is a presentation issue - it's not an essential
> > characteristic of the data. It's not the model's job to decide that all
> clients should get back posts in the last year, and hence that's
> > what its find method should return.
> >
> > All business rules don't reside in the model. As soon as you say "we
> don't SHOW posts over a year," you're talking about a presentation
> > rule, not a model rule. Controllers mediate between data and screen -
> they're responsible for getting the data to be shown. That code should
> > exist in the controller and be tested there.
> >
> > On the other hand, the controller should not necessarily be responsible
> for defining what constitutes "all posts in the last year." That
> > should very likely be a named scope.
> >
> > This is all just my humble opinion, of course, and might be utter rubbish
> in any particular real world situation.
> >
> > ///ark
> >
> > _______________________________________________
> > rspec-users mailing list
> > rspec-users at rubyforge.org
> > http://rubyforge.org/mailman/listinfo/rspec-users
> I agree with this.  You generally don't want to screw with the meaning
> of AR::Base.find.  That's really only for situations where you want some
> uniform, transparent behavior (acts_as_versioned, acts_as_paranoid,
> etc).
> The view shouldn't really care about this stuff either...it should just
> take a list of posts and display them.
> It's up to the controller to ask the model for what it wants to push to
> the view.  So your finder can be something like Post.since(1.year.ago)
> or whatever.
> Andrew, I've noticed a theme in your posts that suggests you feel you
> needn't ever change controller specs.  That's just not true.  Models
> are a distilled representation of the domain, and controllers give
> meaning to it in the context of the application you're working on.
> Models are what your app *is* and controllers are what your app *does*.
> When you change what your app does, you're going to have to change some
> specs and production code along with it.
> Keep in mind that if you went with a state-based test, you would *still*
> have to modify the controller spec.  It wouldn't be good enough to
> expect a post to be displayed.  You'd have to create a post with a
> created_at of over a year ago, and one within the last year, and expect
> that the former is not shown while the latter is.
> Also, I'd like to point out that if you *really* wanted Post.find(:all)
> to only return posts in the last year, you wouldn't actually have to
> change the controller spec :)  You would just have to write a model spec
> for that behavior.
> Pat
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20081211/cfec0f77/attachment.html>

More information about the rspec-users mailing list