简体   繁体   中英

Testing an association model helper method rails rspec

I have two models, User and Account .

# account.rb
belongs_to :user

# user.rb
has_one :account

Account has an attribute name . And in my views, I was calling current_user.account.name multiple times, and I heard that's not the great of a way to do it. So I was incredibly swift, and I created the following method in my user.rb

def account_name
  self.account.name
end

So now in my view, I can simply call current_user.account_name , and if the association changes, I only update it in one place. BUT my question is, do I test this method? If I do, how do I test it without any mystery guests?

I agree there is nothing wrong with current_user.account.name - while Sandi Metz would tell us "User knows too much about Account" this is kind of the thing you can't really avoid w/ Active Record.

If you found you were doing a lot of these methods all over the User model you could use the rails delegate method:

  delegate :name, :to => :account, :prefix => true

using the :prefix => true option will prefix the method in the User model so it is account_name. In this case I would assume you could write a very simple unit test on the method that it returns something just incase the attribute in account would ever change your test would fail so you would know you need to update the delegate method.

  1. There's nothing wrong with current_user.account.name
  2. There's no difference between calling it as current_user.account.name, or making current_user.account_name call it for you
  3. You're probably not calling current_user in the model, like you say
  4. You should have a spec for it if you use it

Personally I see no good reason for any of this. Just use current_user.account.name.

If you are worrying about efficiency, have current_user return a user that joins account.

This is going to be a bit off-topic. So, apologies in advance if it's not interesting or helpful.

TL;DR: Don't put knowledge of your models in your views. Keep your controllers skinny. Here's how I've been doing it.

In my current project, I've been working to make sure my views have absolutely no knowledge of anything about the rest of the system (to reduce coupling). This way, if you decide to change how you implement something (say, current_user.account.name versus current_user.account_name ), then you don't have to go into your views and make changes.

Every controller action provides a @results hash that contains everything the view needs to render correctly. The structure of the @results hash is essentially a contract between the view and the controller.

So, in my controller, @results might look something like {current_user: {account: {name: 'foo'}}} . And in my view, I'd do something like @results[:current_user][:account][:name] . I like using a HashWithIndifferentAccess so I could also do @results['current_user']['account']['name'] and not have things blow up or misbehave.

Also, I've been moving as much logic as I can out of controllers into service objects (I call them 'managers'). I find my managers (which are POROs) a lot easier to test than controllers. So, I might have:

# app/controllers/some_controller.rb
class SomeController
  def create
    @results = SomeManager.create(params)
    if @results[:success]
      # happy routing
    else
      # sad routing
    end
  end
end

Now, my controllers are super skinny and contain no logic other than routing. They don't know anything about my models. (In fact, almost all of my controller actions look exactly the same with essentially the same six lines of code.) Again, I like this because it creates separation.

Naturally, I need the manager:

#app/managers/some_manager.rb
class SomeManager
  class << self
    def create(params)
      # do stuff that ends up creating the @results hash
      # if things went well, the return will include success: true
      # if things did not go well, the return will not include a :success key
    end
  end
end

So, in truth, the structure of @results is a contract between the view and the manager, not between the view and the controller.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM