【问题标题】:How to test controller actions with complicated queries?如何用复杂的查询测试控制器动作?
【发布时间】:2011-04-13 20:55:44
【问题描述】:

假设我有以下动作:

def index
  @posts = Post.joins(:tags).where(:tags => {:id => params[:tag_id]})
end

它将@posts 暴露给视图,该视图将显示每个带有给定标签的帖子。

一切正常,但我一直在努力找出测试它的最佳方法。

我不太喜欢嘲讽,因为如果我将那行更改为:

@posts = Post.where(:tags => {:id => params[:tag_id]}).joins(:tags)

我真的不想访问数据库,因为它会降低测试速度,但我正在考虑将查询提取到模型内的方法中,并在那里测试它是否是唯一的方法.

编辑: 是的,我知道在这种情况下我可以使用Tag.find(params[:tag_id]),但这不是问题所在。我只是不想在查询中引入另一个模型,并使其难以解释偏离真正问题的焦点,即:我们应该在控制器中保留复杂的查询吗?如果是这样,最好的测试方法是什么?

【问题讨论】:

  • 一个附带问题,为什么不:@posts = Tag.find(params[:tag_id]).posts。感觉更自然,几乎不需要在那里进行测试。
  • 复杂查询应该是模型中的范围,而不是控制器中。
  • @tokland 这只是一个带有joinswhere 的查询示例,而不是真正的代码。我只是试图让它变得简单,这样就可以很容易地解释它的作用。
  • 您应该将所有复杂的业务逻辑移出控制器并进入模型。所以这个问题真的变成了:我如何在模型中测试这个?

标签: ruby-on-rails ruby testing controller functional-testing


【解决方案1】:

这是我喜欢做的。一般来说,我喜欢在我的测试中集成数据库测试(虽然有些人不同意,但我个人喜欢它)。我会创建 3 个工厂(:post),也许还有一些标签作为虚拟数据,然后我会调用控制器并检查收到的 @posts 是否是我所期望的。

【讨论】:

    【解决方案2】:

    所以,根据 cmets 提取到模型是最好的做法。我就是这样做的:

    post.rb:

    class Post
      scope :tagged_as, lambda {|tag_id| where(:tag_id => tag_id)}
    end
    

    posts.yml:

    one:
      title: Post 1
      tags: one, three
    
    two:
      title: Post 2
      tags: two, three
    

    post_test.rb:

    test 'find by tag' do
      posts = Post.tagged_as(tags(:one))
      assert_includes posts, posts(:one)
      refute_includes posts, posts(:two)
    end
    

    【讨论】:

    • 你到底为什么要重塑联想而不是 tag.posts?更不用说你要求一个标签ID,而是提供一个标签对象。
    • 你可以做 Tag.find(tag_id).posts。
    • @Rein 查看我在 cmets 中给 tokland 的答案。这不是真正的代码。我只是试着做一个简单的查询,这样每个人都会专注于控制器与复杂查询的测试问题。不幸的是,我认为结果正好相反,所以我要编辑这个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-22
    相关资源
    最近更新 更多