rspec test

时间:2016-07-31 19:16:06

标签: ruby-on-rails rspec-rails

这是我在编程多年中看到的最奇怪的错误。请忽略我在下面使用的奇怪习语,因为我正在使用开拓者,一个Rails的编程框架:

以下代码

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

抛出一个未定义的方法`id'对于nil:行中的NilClass

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

但行

p app.id 

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

可能会发生什么?

1 个答案:

答案 0 :(得分:1)

这一行:

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

创建一个名为app的方法,在调用时返回App。 (它会记住结果,因此在初始调用后它将始终返回相同的App。)

这一行:

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

创建名为app新本地变量,该方法与方法app不明确。根据{{​​3}}:

  

在Ruby中,局部变量名和方法名几乎相同。如果   你还没有分配给ruby所假设的其中一个模棱两可的名字   你想打电话给一个方法。一旦你分配了名称ruby   将假设您希望引用局部变量。

     

解析器遇到局部变量时会创建局部变量   分配,而不是在分配发生时:

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

这是我能想到的最简单的代码,可以重现这个问题:

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

但我认为这两者都比首先不创建命名冲突更糟糕。