rspec 测试 has_many :through 和 after_save

2024-05-23

我有一个(我认为)相对简单的has_many :through与连接表的关系:

class User < ActiveRecord::Base
  has_many :user_following_thing_relationships
  has_many :things, :through => :user_following_thing_relationships
end

class Thing < ActiveRecord::Base
  has_many :user_following_thing_relationships
  has_many :followers, :through => :user_following_thing_relationships, :source => :user
end

class UserFollowingThingRelationship < ActiveRecord::Base
  belongs_to :thing
  belongs_to :user
end

这些 rspec 测试(我知道这些不一定是好的测试,这些只是为了说明正在发生的事情):

describe Thing do     
  before(:each) do
    @user = User.create!(:name => "Fred")
    @thing = Thing.create!(:name => "Foo")    
    @user.things << @thing
  end

  it "should have created a relationship" do
    UserFollowingThingRelationship.first.user.should == @user
    UserFollowingThingRelationship.first.thing.should == @thing
  end

  it "should have followers" do
    @thing.followers.should == [@user]
  end     
end

这工作正常,直到我添加一个after_save to the Thing引用其的模型followers。也就是说,如果我这样做

class Thing < ActiveRecord::Base
  after_save :do_stuff
  has_many :user_following_thing_relationships
  has_many :followers, :through => :user_following_thing_relationships, :source => :user

  def do_stuff
    followers.each { |f| puts "I'm followed by #{f.name}" }
  end
end

然后第二个测试失败 - 即关系仍然添加到连接表中,但是@thing.followers返回一个空数组。此外,回调的那部分永远不会被调用(就像followers模型内为空)。如果我添加一个puts "HI"在之前的回调中followers.each行,“HI”显示在标准输出上,所以我知道正在调用回调。如果我注释掉followers.each行,然后测试再次通过。

如果我通过控制台执行此操作,则效果很好。即,我可以做

>> t = Thing.create!(:name => "Foo")
>> t.followers # []
>> u = User.create!(:name => "Bar")
>> u.things << t
>> t.followers  # [u]
>> t.save    # just to be super duper sure that the callback is triggered
>> t.followers  # still [u]

为什么 rspec 会失败?我做错了什么可怕的事情吗?

Update

如果我手动定义一切正常Thing#followers as

def followers
  user_following_thing_relationships.all.map{ |r| r.user }
end

这让我相信也许我正在定义我的has_many :through with :source错误地?

Update

我创建了一个最小的示例项目并将其放在 github 上:https://github.com/dantswain/RspecHasMany https://github.com/dantswain/RspecHasMany

另一个更新

非常感谢@PeterNixey 和@kikuchiyo 在下面提出的建议。最终的答案是两个答案的组合,我希望我可以在它们之间分配功劳。我已经用我认为最干净的解决方案更新了 github 项目并推送了更改:https://github.com/dantswain/RspecHasMany https://github.com/dantswain/RspecHasMany

如果有人能给我一个关于这里发生的事情的真正可靠的解释,我仍然会喜欢它。对我来说最麻烦的是,为什么在最初的问题陈述中,如果我注释掉对的引用,所有内容(除了回调本身的操作)都会起作用followers.


我过去也遇到过类似的问题,这些问题已通过重新加载关联(而不是父对象)来解决。

重新加载后还能用吗thing.followers在 RSpec 中?

it "should have followers" do
  @thing.followers.reload
  @thing.followers.should == [@user]
end 

EDIT

如果(正如您所提到的)您遇到回调未触发的问题,那么您可以在对象本身中进行重新加载:

class Thing < ActiveRecord::Base
  after_save { followers.reload}
  after_save :do_stuff
  ...
end

or

class Thing < ActiveRecord::Base
  ...
  def do_stuff
    followers.reload
    ...
  end
end

我不知道为什么 RSpec 存在不重新加载关联的问题,但我自己也遇到过同样类型的问题

Edit 2

尽管 @dantswain 证实followers.reload帮助缓解了一些问题,但仍然没有解决所有问题。

为此,该解决方案需要 @kikuchiyo 的修复,需要调用save完成回调后Thing:

describe Thing do
  before :each do
    ...
    @user.things << @thing
    @thing.run_callbacks(:save)
  end 
  ...
end

最终建议

我相信这是因为使用了<< on a has_many_through手术。我不认为<<实际上应该触发你的after_save事件:

您当前的代码是这样的:

describe Thing do
  before(:each) do
    @user = User.create!(:name => "Fred")
    @thing = Thing.create!(:name => "Foo")    
    @user.things << @thing
  end
end

class Thing < ActiveRecord::Base
  after_save :do_stuff
  ...

  def do_stuff
   followers.each { |f| puts "I'm followed by #{f.name}" }
  end
end

问题是do_stuff没有接到电话。我认为这是正确的行为。

让我们看一下 RSpec:

describe Thing do
  before(:each) do
    @user = User.create!(:name => "Fred")
    # user is created and saved

    @thing = Thing.create!(:name => "Foo")    
    # thing is created and saved

    @user.things << @thing
    # user_thing_relationship is created and saved
    # no call is made to @user.save since nothing is updated on the user
  end
end

问题是第三步实际上并不需要thing要重新保存的对象 - 它只是在连接表中创建一个条目。

如果您想确保 @user 确实调用 save 您可能会得到您想要的效果,如下所示:

describe Thing do
  before(:each) do
    @thing = Thing.create!(:name => "Foo")    
    # thing is created and saved

    @user = User.create!(:name => "Fred")
    # user is created BUT NOT SAVED

    @user.things << @thing
    # user_thing_relationship is created and saved
    # @user.save is also called as part of the addition
  end
end

您可能还会发现after_save事实上,回调位于错误的对象上,您更希望将其放在关系对象上。最后,如果回调确实属于用户并且您确实需要在创建关系后触发它,您可以使用touch创建新关系时更新用户。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

rspec 测试 has_many :through 和 after_save 的相关文章

  • 如何检查水豚中的复选框?

    我正在使用 Rspec 和水豚 我怎样才能写一个步骤来检查checkbox 我试过了check按价值但它找不到我的checkbox 我不知道该怎么做 因为我实际上有相同的 ID 但值不同 这是代码
  • Rails 中的并行方法

    我的 Rails Web 应用程序有数十种方法 从调用 API 到处理查询结果 这些方法具有以下结构 def method one batch query API process data end def method nth batch
  • 如何从数组中删除空白元素?

    我有以下数组 cities Kathmandu Pokhara Dharan Butwal 我想从数组中删除空白元素并想要以下结果 cities Kathmandu Pokhara Dharan Butwal 有没有类似的方法compact
  • gem install rmagick 在 OS X El Capitan 上失败

    几天前我升级到 El Capitan 并运行了 brew update brew upgrade 它更新了 imagemagick 导致 ruby 的 rmagick gem 停止工作 我想没问题 我就跑 gem install rmagi
  • Rails:渲染不起作用,仍然出现“模板丢失”

    我目前正在学习 Rails 指南 我完成了这些步骤 但仍然遇到错误 我的 Ruby 版本是ruby 2 1 1p76Rails 版本是4 0 4 按照指南的指示 我创建了一个Article Controller class Articles
  • (在 Ruby 中)允许混合类方法访问类常量

    我有一个为其定义常量的类 然后我定义了一个类方法来访问该类常量 这很好用 一个例子 usr bin env ruby class NonInstantiableClass Const hello world class lt lt self
  • Capybara 的 has_selector 有哪些选项?

    我在 RSpec 中遇到此错误 有没有任何文档have selector解释了选项哈希中的每个键以及它到底有什么作用 invalid keys content should be one of text visible between co
  • 如何在 JS Rails 响应中包含 HTML?

    我有一个响应 HTML 和 JS AJAX 查询的 FooController app controllers foo controller rb class FooController lt ApplicationController l
  • HABTM 关系和accepts_nested_attributes_for

    我有一个可以让我创建的表单新博客文章我希望能够创造新类别来自同一个表格 我在帖子和类别之间有一个习惯关系 这就是我遇到麻烦的原因 我有以下2个型号 class Post lt ActiveRecord Base has and belong
  • Ruby on Rails 3 - 为每个请求重新加载 lib 目录

    我正在为 Rails 3 应用程序创建一个新引擎 正如您所猜测的 该引擎位于我的应用程序的 lib 目录中 但是 我在开发它时遇到了一些问题 事实上 每次更改引擎中的某些内容时 我都需要重新启动服务器 有办法避免这种情况吗 我可以强制rai
  • save_and_open_page 已停止提供我的 CSS

    我的测试设置工作得很好 每当我打电话时 都能提供正确格式的 css 页面save and open page从测试中 然后 我设置了一些 javascript 测试 并对我的设置进行了一些更改 抱歉 我无法详细说明所有内容 我没有足够详细地
  • 如何从rails控制台将数据添加到数据库

    我有一个User model gt gt u User new gt
  • 如何编写一个在安装 RubyGem 时调用的钩子?

    我想编写一个 Ruby 片段 当我的 Gem 首次安装时运行 sudo gem install mygem 能做到吗 看起来并没有真正支持 我发现了一个 post install message 属性 您应该能够在 gem 规范中设置该属性
  • Rails 3:使用 Simple_form 如何创建一个向specialities#create 发布帖子的表单?

    如何使用 Simple form 创建一个向specialities create 发布帖子的表单 我试过这个
  • RSpec 请求规范发布一个空数组

    我目前正在 Rails 中开发 API 端点 如果我需要的数据无效 我想确保端点响应具有正确的错误状态 我需要一个 id 数组 无效值之一是空数组 Valid vendor district ids 2 4 5 6 Invalid vend
  • 回滚后是否应该删除迁移

    我对 ruby 和 Rails 相当陌生 刚刚开始了解迁移 我的问题是回滚后删除迁移的最佳实践或正确时间是什么 到目前为止 我读到的内容是回滚后是否删除迁移的观点问题 但是在团队中工作时删除迁移是否有任何重大影响 以及保留迁移文件相对于删除
  • 如何在服务调用后检查 rspec 中的数组更改?

    目标很简单 例如我们有一个数组 name ghost state rejected name donkey state rejected 运行服务调用后UpdateAllUsers 这会将所有用户更改为 accepted name ghos
  • 下载所有 gems 依赖项

    我想通过下载任何所需的文件并将它们带到另一台计算机来安装指南针没有互联网连接 我已经下载了指南针的源包 当我在未连接的计算机上运行 gem 时 它抱怨缺少依赖项 有什么解决办法吗 这正是我遇到的问题 经过一段时间的搜索后 我找到了一个可以使
  • 在 ActiveAdmin 或打印解决方案中动态更改分页

    我是 Activeadmin 和 Rails 的新手 我需要一些帮助 我有一个分页模型 我想允许用户更改分页值或完全禁用它 这样它就可以打印 到打印机 所有记录 或过滤后的记录 我知道我可以在 before filter 中使用 per p
  • 为什么 Rails 中的区域设置充当全局(使用 Thin 时)?

    我刚刚意识到在控制器中设置区域设置的推荐 Rails 方法 before filter set locale def set locale I18n locale params locale I18n default locale end

随机推荐