简体   繁体   English

测试关联模型助手方法Rails RSpec

[英]Testing an association model helper method rails rspec

I have two models, User and Account . 我有两个模型, UserAccount

# account.rb
belongs_to :user

# user.rb
has_one :account

Account has an attribute name . Account具有属性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. 在我看来,我已经多次调用current_user.account.name ,并且听说这不是一种很好的方法。 So I was incredibly swift, and I created the following method in my user.rb 所以我非常快,我在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. 因此,现在在我看来,我可以简单地调用current_user.account_name ,并且如果关联发生更改,我只会在一个地方进行更新。 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. 我同意current_user.account.name没什么问题-尽管Sandi Metz会告诉我们“用户对帐户了解太多”,这是您无法避免使用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: 如果发现您整个User模型中都使用了许多这些方法则可以使用rails委托方法:

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

using the :prefix => true option will prefix the method in the User model so it is account_name. 使用:prefix => true选项将在用户模型中将该方法添加为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 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 将其称为current_user.account.name或使current_user.account_name为其命名没有区别
  3. You're probably not calling current_user in the model, like you say 您可能没有在模型中调用current_user,就像您说的那样
  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. 只需使用current_user.account.name。

If you are worrying about efficiency, have current_user return a user that joins account. 如果您担心效率,请让current_user返回一个加入帐户的用户。

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. TL; DR:不要在您的视图中放入模型知识。 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. 这样,如果您决定更改实现方式(例如current_user.account.namecurrent_user.account_name ),则无需进入视图并进行更改。

Every controller action provides a @results hash that contains everything the view needs to render correctly. 每个控制器操作都提供一个@results哈希,其中包含视图需要正确呈现的所有内容。 The structure of the @results hash is essentially a contract between the view and the controller. @results哈希的结构本质上是视图和控制器之间的协定。

So, in my controller, @results might look something like {current_user: {account: {name: 'foo'}}} . 因此,在我的控制器中, @results可能类似于{current_user: {account: {name: 'foo'}}} And in my view, I'd do something like @results[:current_user][:account][:name] . 在我看来,我会做类似@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. 我喜欢使用HashWithIndifferentAccess所以我也可以执行@results['current_user']['account']['name']且不会发生异常或行为异常。

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. 我发现我的经理(即PORO)比控制器要容易得多。 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. 因此,实际上, @results的结构是视图和管理器之间的契约,而不是视图和控制器之间的契约。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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