Rails 测试 has_many 关联失败

发布于 2024-12-06 09:01:09 字数 1899 浏览 0 评论 0原文

我有 2 个型号。一个用户和一个任务。这是它们的代码:

class User < ActiveRecord::Base
  has_many :tasks
  has_many :assigned_tasks, :class_name => 'Task', :foreign_key => 'assigned_user_id'
end

class Task < ActiveRecord::Base
  belongs_to :user
  belongs_to :assigned_user, :class_name => 'User', :foreign_key => 'assigned_user_id'
end

该架构非常明显,但为了保持一致性,它看起来是这样的:

ActiveRecord::Schema.define(:version => 20110925050945) do
  create_table "tasks", :force => true do |t|
    t.string   "name"
    t.integer  "user_id"
    t.integer  "assigned_user_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "users", :force => true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end
end

我为分配的任务关系添加了一个测试用例。它看起来像这样:

class UserTest < ActiveSupport::TestCase
  test "assigned tasks" do
    u1 = User.create(:name => 'john')
    u2 = User.create(:name => 'dave')

    assert_empty u2.assigned_tasks # LOOK AT ME

    task = u1.tasks.create(:name => 'some task', :assigned_user_id => u2.id)

    assert_equal 1, u2.assigned_tasks.size
  end
end

现在,这个测试用例失败了。最后一个断言失败了。如果我删除之前的断言(标记为“LOOK AT ME”),则该测试会顺利通过。如果我将此行更改为 assert u2.assigned_tasks,它也会顺利通过。这意味着当且仅当针对 u2.assigned_tasks 调用 empty? 时,它才会出现中断。如果该断言通过,则下一个断言就会失败。失败如下:

UserTest:
     FAIL assigned tasks (0.12s) 
          <1> expected but was
<0>.
          test/unit/user_test.rb:12:in `block in <class:UserTest>'

因此,一旦在原始 u2.assigned_tasks 数组上调用 empty? ,任务实际上并未添加/与其分配的用户关联。然而,这在控制台中似乎工作正常。

如果我在这里完全忽略了一些简单的事情,我很抱歉,但我真的无法理解这一点。任何方向正确的观点都会非常有帮助。谢谢

PS:带有普通应用程序的 Rails 3.1

I have 2 models. A User and a Task. Here's the code for them both:

class User < ActiveRecord::Base
  has_many :tasks
  has_many :assigned_tasks, :class_name => 'Task', :foreign_key => 'assigned_user_id'
end

class Task < ActiveRecord::Base
  belongs_to :user
  belongs_to :assigned_user, :class_name => 'User', :foreign_key => 'assigned_user_id'
end

The schema is quite obvious, but for consistency, this is how it looks:

ActiveRecord::Schema.define(:version => 20110925050945) do
  create_table "tasks", :force => true do |t|
    t.string   "name"
    t.integer  "user_id"
    t.integer  "assigned_user_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "users", :force => true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end
end

I've added a test case for the assigned_tasks relationship. It looks like this:

class UserTest < ActiveSupport::TestCase
  test "assigned tasks" do
    u1 = User.create(:name => 'john')
    u2 = User.create(:name => 'dave')

    assert_empty u2.assigned_tasks # LOOK AT ME

    task = u1.tasks.create(:name => 'some task', :assigned_user_id => u2.id)

    assert_equal 1, u2.assigned_tasks.size
  end
end

Now, this test case fails. It fails on the last assertion. If I remove the previous assertion (marked 'LOOK AT ME'), this test passes fine. It also passes fine if I change this line to assert u2.assigned_tasks. Meaning it appears to break when, and only when, empty? is called against u2.assigned_tasks. Where that assertion passes, the following one fails. Here's the failure:

UserTest:
     FAIL assigned tasks (0.12s) 
          <1> expected but was
<0>.
          test/unit/user_test.rb:12:in `block in <class:UserTest>'

So, it appears once empty? is called on the original u2.assigned_tasks Array, the task is not actually added/associated with it's assigned user. This however appears to work fine in console.

Apologies if I'm completely overlooking something simple here, but I really can't make any sense of this. Any points in the right direction would be extremely helpful. Thanks

PS: Rails 3.1 with a vanilla application

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

怪我太投入 2024-12-13 09:01:09

您需要重新加载 signed_tasksu2

# This line causes assigned_tasks to be loaded and cached on u2.  Not the calling
# of empty?, but rather the loading of the association.
assert_empty u2.assigned_tasks

# but then you actually make the task here
task = u1.tasks.create(:name => 'some task', :assigned_user_id => u2.id)

# so when this assertion happens, u2 already has an empty set of tasks cached, 
# and fails
assert_equal 1, u2.assigned_tasks.size

# however either of these should pass
assert_equal 1, u2.assigned_tasks(true).size
assert_equal 1, u2.reload.assigned_tasks.size

inverse_of 选项用于改进内存中关联行为,并且还可能解决您的问题(无需重新加载)。请在此处了解相关内容。它看起来像这样(但我再次不确定它在这种情况下是否有效):

# on User
has_many :assigned_tasks, ..., :inverse_of => :assigned_user

# on Task
belongs_to :assigned_user, ..., :inverse_of => :assigned_tasks

# in your test you might have to change the task creation to:
u1.tasks.create(:name => 'some task', :assigned_user => u2)

You need to reload the assigned_tasks, or u2.

# This line causes assigned_tasks to be loaded and cached on u2.  Not the calling
# of empty?, but rather the loading of the association.
assert_empty u2.assigned_tasks

# but then you actually make the task here
task = u1.tasks.create(:name => 'some task', :assigned_user_id => u2.id)

# so when this assertion happens, u2 already has an empty set of tasks cached, 
# and fails
assert_equal 1, u2.assigned_tasks.size

# however either of these should pass
assert_equal 1, u2.assigned_tasks(true).size
assert_equal 1, u2.reload.assigned_tasks.size

The inverse_of option serves to improve in-memory association behavior, and might also solve your problem (without reloading). Read about that here. It would look something like this (but again I'm not positive it will work in this case):

# on User
has_many :assigned_tasks, ..., :inverse_of => :assigned_user

# on Task
belongs_to :assigned_user, ..., :inverse_of => :assigned_tasks

# in your test you might have to change the task creation to:
u1.tasks.create(:name => 'some task', :assigned_user => u2)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文