ruby - if + else 和 if -> unless 不一致

标签 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

方法 2 -- if 后跟 unless

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

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

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

编辑:

忘记添加实际测试(使用方法 2):

存在以下 HTML 标记:

<div class="alert alert-success">Profile updated</div>

我运行 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

失败的消息如下:

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)>'

当 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

使用这种方法的行为与方法 2 相同 - 第一个测试失败,然后第三次通过。

输出如下:

In if, message is: nil
In unless, message is: "Profile updated"

所以控制流看起来没问题,但是

page.should(have_selector('div.alert.alert-success'))

失败,即使它通过了匹配器之外。这真是一个谜。

最终编辑:

只是为了回应已批准的答案 - 当我这样切换代码时:

page.should have_selector('div.alert.alert-success', text: message) unless message.nil? 
page.should have_selector('div.alert.alert-success') if message.nil?

测试看起来像这样:

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

所以我认为确实最后一行,当它不是真的时,被评估为零,这导致了整个困惑。无论如何,第一种方法更好,但我很高兴我已经解决了这个问题:)

最佳答案

这是 RSpec 的正确行为,即使它看起来出乎意料。

考虑这段代码:

x = nil
"foo" if x.nil?
"bar" unless x.nil?
#=> 
"foo"
nil

当条件为假时,...unless 语句返回 nil

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

那是你的匹配 block 中的最后一行,所以你的匹配 block 返回 nil。

然后 RSpec 看到您的匹配 block 返回 nil,RSpec 认为这与 false 相同,因此 RSpec 报告您的自定义匹配器失败。

关于ruby - if + else 和 if -> unless 不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13663163/

相关文章:

testing - 如何单击溢出 :hidden in cucumber/capybara 的元素

ruby-on-rails - Capybara/Cucumber 无法点击 twitter bootstrap rails 应用程序中的链接/按钮

ruby-on-rails - 加载错误 : cannot load such file -- gserver

html - 我打印 MySQL 数据的 CGI 脚本有什么问题?

ruby-on-rails - 有没有办法强制 RSpec 显示中间结果?

ruby-on-rails - Rspec 停止工作 - `<module:ActionController>':未初始化的常量 ActionController::Metal (NameError)

css - 选择 <option></option> 标签内的内容

ruby - 降级 gem 时,bundler 是否可以自动降级 gem 的依赖项?

ruby-on-rails - 尽管 URI 正确,但 Gravatar 图像未显示 - Ruby on Rails

ruby-on-rails - 如何使用 RSpecs valid_session?