Recently some of my controller actions have taken on a definite new shape. Particularly when the action is a read-only query of the app’s state. Such actions tend to make up the bulk of my apps, and they can be simple because they are unlikely to “fail” in any predictable way. Here’s an example from my wiki app:
class CardsController < ApplicationController | |
def recently_changed | |
render 'recently_changed', locals: { | |
cards: RecentlyChanged.new(wiki).cards, | |
} | |
end | |
end |
This has a couple of significant plus points: First, no instance variables, so both the controller action and the view are more readable and easier to refactor. Second, no instance variables! So there’s a clear, documented, named interface between the controller and the view. And third, this is so transparently readable that I never bother to test it.
The wiki used in the above action is a repository, built in a memoizing helper method that most of the controllers use:
class ApplicationController < ActionController | |
def wiki | |
@wiki ||= if (current_user.admin?) | |
AdminWiki.new | |
else | |
ReadonlyWiki.new | |
end | |
end | |
end |
In this case the correct kind of repository is created for the current user, and all of the other code in this request sits on top of that. So the controller action, helped by the memoized repository builder, effectively constructs an entire hexagonal architecture for each request, and the domain logic is thus blissfully unaware of its context.
Here’s a slightly bigger example. This is for a page that shows a variety of informative graphs about the wiki; and because I may want to re-organise my admin pages in the future, each graph’s data is built independently of the others:
class StatisticsController < ApplicationController | |
def traffic | |
hits_by_date = statistics.page_hits_by_date | |
render 'traffic', locals: { | |
weekly_page_hits: statistics.weekly_page_hits(hits_by_date), | |
weekday_hits: statistics.hits_by_weekday(hits_by_date), | |
time_of_day_hits: statistics.hits_by_time_of_day, | |
} | |
end | |
end |
That’s the most complex query controller action I have, and I maintain that it’s so simple I don’t need to test it. Would you?
In both examples I agree, what is there to test?
I try and treat controllers like main – just instantiate something and fire a method off. Unit testing would have very little benefit here – this is just an exercise of “wiring stuff up”. If you cock this up the your stats won’t work when you check manually.
I personally find a lot of resistance against this – using your first example people want to know that “cards” is invoked – all I find is this makes refactoring difficult. My argument is I don’t test main, why would I test controllers?