if + else和if之间的不一致 - >除非

STT*_*STT 8 ruby rspec capybara railstutorial.org

我今天遇到了一个问题,我定义了我解决的自定义RSpec匹配器,但实际上看不出为什么其中一种方法有效,而另一种方法无效,这里是代码:

方法1 - if + else:

RSpec::Matchers.define :have_success_message do |message|
  match do |page|
    if message.nil?
      page.should have_selector('div.alert.alert-success')
    else
      page.should have_selector('div.alert.alert-success', text: message)
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

方法2 - 如果遵循,则除非

RSpec::Matchers.define :have_success_message do |message|
  match do |page|
    page.should have_selector('div.alert.alert-success') if message.nil?
    page.should have_selector('div.alert.alert-success', text: message) unless message.nil?
  end
end
Run Code Online (Sandbox Code Playgroud)

我认为第一种方法更好,因为它只检查一次条件,但是,结果应该是相同的,对吧?

好吧,事实证明,第一种方法通过了测试,而第二种方法的测试则没有.我完全不知道为什么会这样,如果有人能够对此有所了解,我会很高兴.

编辑:

忘了添加实际测试(方法2):

使用以下HTML标记:

<div class="alert alert-success">Profile updated</div>
Run Code Online (Sandbox Code Playgroud)

我运行了4个单独的测试:

it { should have_success_message } # fails
it { should have_success_message('Profile updated') } # passes
it { should have_selector('div.alert.alert-success') } # passes
it { should have_selector('div.alert.alert-success', text: "Profile updated") } # passes
Run Code Online (Sandbox Code Playgroud)

失败是以下消息:

1) User pages edit with valid information 
 Failure/Error: it { should have_success_message }
   expected #<Capybara::Session> to have success message
 # ./spec/requests/user_pages_spec.rb:80:in `block (5 levels) in <top (required)>'
Run Code Online (Sandbox Code Playgroud)

当HTML标记不存在时,所有4个测试都会失败.

编辑2:

我尝试了另一种方法来验证控制流是否正确:

方法3:

if message.nil?
  puts "In if, message is: #{message.inspect}"
  page.should(have_selector('div.alert.alert-success'))
end
unless message.nil?
  puts "In unless, message is: #{message.inspect}"
  page.should(have_selector('div.alert.alert-success', text: message))
end
Run Code Online (Sandbox Code Playgroud)

使用这种方法,行为与方法2相同 - 第一次测试失败,在3次传递后.

输出如下:

在if中,消息为:nil
In除非,消息为:"Profile updated"

所以控制流看起来不错,但是

page.should(have_selector('div.alert.alert-success'))
Run Code Online (Sandbox Code Playgroud)

失败,即使它通过匹配器外.这真的是一个谜.

最终编辑:

只是回应批准的答案 - 当我切换代码时:

page.should have_selector('div.alert.alert-success', text: message) unless message.nil? 
page.should have_selector('div.alert.alert-success') if message.nil?
Run Code Online (Sandbox Code Playgroud)

测试看起来像这样:

it { should have_success_message } # passes
it { should have_success_message('Profile updated') } # fails
it { should have_selector('div.alert.alert-success') } # passes
it { should have_selector('div.alert.alert-success', text: "Profile updated") } # passes
Run Code Online (Sandbox Code Playgroud)

所以我认为确实最后一行,当它不是真的时,被评估为零,这导致整个混乱.无论如何,第一种方法更好,但我很高兴我有这个问题:)

joe*_*son 6

这是RSpec的正确行为,即使它看起来很意外.

考虑以下代码:

x = nil
"foo" if x.nil?
"bar" unless x.nil?
#=> 
"foo"
nil
Run Code Online (Sandbox Code Playgroud)

...unless语句返回nil时的条件是falsey.

在您的自定义匹配器中,...unless当您的消息为零时,语句将返回nil.

这是匹配块中的最后一行,因此匹配块返回nil.

然后RSpec看到你的匹配块返回nil,RSpec认为它与false相同,因此RSpec报告你的自定义匹配器失败.