ruby on rails-在rspec测试中,变量既为nil,也不是nil



这是我多年编程中见过的最奇怪的错误。请忽略我在下面使用的奇怪习语,因为我正在使用Rails:的编程框架trailblazer

下方的代码

let (:app) { App::Create.(app: {name: "Half-Life", steam_id: "70"}).model }
it 'gets app details from Steam' do
  VCR.use_cassette('app/get') do
    p app.id
    app_id = app.id
    app = App::Get.(id: app_id).model
  end
  expect(app.app_type).to eq('game')
end

如预期工作。

下方的代码

let (:app) { App::Create.(app: {name: "Half-Life", steam_id: "70"}).model }
it 'gets app details from Steam' do
  VCR.use_cassette('app/get') do
    p app.id
    app = App::Get.(id: app.id).model
  end
  expect(app.app_type).to eq('game')
end

在行中为nil:NilClass抛出未定义的方法"id"

app = App::Get.(id: app.id).model

但是线路

p app.id 

就在违规行显示正确的id之前。

可能发生了什么?

此行:

let (:app) { App::Create.(app: {name: "Half-Life", steam_id: "70"}).model }

创建一个名为app的方法,该方法在调用时返回一个App。(它存储结果,因此在初始调用后总是返回相同的App。)

此行:

app = App::Get.(id: app.id).model

创建一个名为app新局部变量,该变量与方法app不明确。根据Ruby文档分配:

在Ruby中,局部变量名和方法名几乎相同。如果你还没有给ruby假定的这些不明确的名字赋值您希望调用一个方法。一旦你指定了ruby这个名字将假定您希望引用局部变量。

当解析器遇到分配,而不是当分配发生时:

因此,当您尝试分配给app时,会创建一个新的局部变量。当赋值的右侧被求值时,app.id尝试调用局部变量app(到目前为止是nil)上的id

以下是我能想到的最简单的代码,它再现了这个问题:

def a
  "Hello"
end
p a.upcase # "HELLO"
a = a.upcase # undefined method `upcase' for nil:NilClass (NoMethodError)

有几种方法可以修复代码。我认为到目前为止最好的解决方案是:

app2 = App::Get.(id: app.id).model

名称app已被一个方法使用。。。不要通过创建具有相同名称的局部变量来隐藏它。

你也可以这样做:

app = App::Get.(id: app().id).model # the parens disambiguate

或者你已经拥有的解决方案:

app_id = app.id # capture the ID before the local variable gets created
app = App::Get.(id: app_id).model

但我认为这两者都比一开始就不创建命名冲突更糟糕。

相关内容

  • 没有找到相关文章

最新更新