RSpec – 除非从pry点调用局部变量,否则测试失败。

huangapple go评论59阅读模式
英文:

RSpec - failing test unless local variable is called from pry point

问题

我有以下的代码:

## teams_controller.rb

def destroy
  ActiveRecord::Base.transaction do
    team_admins = team.team_admins
    binding.pry
    team.destroy!
    team_admins.each(&:update_team_admin_role_if_needed!)
  end

  respond_with_200(team, serializer: V1::User::Management::TeamSerializer)
end

以及相应的规范以确保上述代码的最后一行被执行:

## teams_controller_spec.rb

it 'demotes team admins to employees when needed' do
  team_admin_account = create(:account)
  admin_team_membership = create(:team_membership, team: @team, admin: true, account: team_admin_account)
  team_admin_account.update!(role: Role.team_admin)

  expect { process_destroy(team_id: @team.slug) }
    .to change { team_admin_account.reload.role }
    .from(Role.team_admin)
    .to(Role.employee)
end

当我在我的应用程序中使用上述代码时,它按预期工作,但是规范失败,因为帐户似乎从未更新其角色:

expected `team_admin_account.reload.role` to have changed from #<Role id: 4, add_to_first_user_in_organisation: false, title: "Team admin", created_at: "2020-01-03 09:04:28", updated_at: "2020-01-03 09:04:28", management: false, cms_access: false> to #<Role id: 3, add_to_first_user_in_organisation: false, title: "Employee", created_at: "2020-01-03 09:04:28", updated_at: "2020-01-03 09:04:28", management: false, cms_access: false>, but did not change

当我在我的规范中触发 pry 点并立即退出时,规范失败。同样,当没有 pry 点时也会失败。

但是,当我在 pry 点中输入 team_admins(这会返回我在规范中创建的一个 team_admin),然后退出规范时,规范通过了,并且帐户的角色得到了更新。

有人知道为什么手动调用 team_admins 会使我的规范通过吗?

谢谢提前回答

编辑:

对代码的以下更改也使规范通过:

def destroy
  ActiveRecord::Base.transaction do
    team_admins = team.team_admins
    puts team_admins                        ## <---- 添加这一行使规范通过
    team.destroy!
    team_admins.each(&:update_team_admin_role_if_needed!)
  end

  respond_with_200(team, serializer: V1::User::Management::TeamSerializer)
end
英文:

I have the following code:

## teams_controller.rb

def destroy
  ActiveRecord::Base.transaction do
    team_admins = team.team_admins
    binding.pry
    team.destroy!
    team_admins.each(&:update_team_admin_role_if_needed!)
  end

  respond_with_200(team, serializer: V1::User::Management::TeamSerializer)
end

And the corresponding spec to ensure the last line of the above code fires:

## teams_controller_spec.rb

it 'demotes team admins to employees when needed' do
  team_admin_account = create(:account)
  admin_team_membership = create(:team_membership, team: @team, admin: true, account: team_admin_account)
  team_admin_account.update!(role: Role.team_admin)

  expect { process_destroy(team_id: @team.slug) }
    .to change { team_admin_account.reload.role }
    .from(Role.team_admin)
    .to(Role.employee)
end

When I use the above code in my application it works as expected, however the spec fails as the account apparently never has their role updated:

 expected `team_admin_account.reload.role` to have changed from #<Role id: 4, add_to_first_user_in_organisation: false, title: "Team admin", created_at: "2020-01-03 09:04:28", updated_at: "2020-01-03 09:04:28", management: false, cms_access: false> to #<Role id: 3, add_to_first_user_in_organisation: false, title: "Employee", created_at: "2020-01-03 09:04:28", updated_at: "2020-01-03 09:04:28", management: false, cms_access: false>, but did not change

When I hit the pry point in my spec and quit out straight away, the spec fails. Likewise when there is no pry point.

However when I enter team_admins at the pry point (which returns the one team_admin I create in my spec) and then quit out of the spec, the spec passes and the account has their role updated.

Anyone have any idea why manually calling team_admins makes my spec pass?

Thanks in advance

EDIT:

The following change to the code also makes the spec pass:

def destroy
  ActiveRecord::Base.transaction do
    team_admins = team.team_admins
    puts team_admins                        ## <---- Adding this makes the spec pass
    team.destroy!
    team_admins.each(&:update_team_admin_role_if_needed!)
  end

  respond_with_200(team, serializer: V1::User::Management::TeamSerializer)
end

答案1

得分: 1

team_admins 是来自 team 的 AssociationRelation。直到被引用,比如使用 eachputs,它才会执行查询。

在调用 team_admins.each 之前调用了 team.destroy!。所以当 team_admins.each 执行时,已经没有 team 了,因此也没有了 team_admins。你可以通过观察 logs/test.log 并查看查询以及它们何时执行来验证这一点。

恭喜,你发现了一个 bug。在销毁 team 之前执行 team_admins.each

英文:

team_admins is an AssociationRelation from team. This does not execute a query until referenced, such as with each or puts.

team.destroy! is called before team_admins.each is called. So when team_admins.each executes there is no more team and thus no team_admins. You should be able to verify this by watching logs/test.log and looking at the queries and when they are executed.

Congratulations, you've found a bug. Execute team_admins.each before destroying team.

huangapple
  • 本文由 发表于 2020年1月3日 17:11:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/59575783.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定