您能同时在RSpec中测试状态变化和返回值吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了您能同时在RSpec中测试状态变化和返回值吗?相关的知识,希望对你有一定的参考价值。

假设我有一个方法MyKlass#do_thing,我想在测试中调用一次(因为它可能会改变状态),并且应该在成功的状态更改时返回true,否则返回false。我想写一个看起来像这样的规范:

it "Updates myvalue if condition is met" do
  wojit = MyKlass.new

  # ... assuming condition is met
  expect { wojit.do_thing }.to change { wojit.value }.and.be true
end

但是这种特殊方法会产生ArgumentError,因为#and需要1个参数。

我可以使它与下面的憎恶一起工作:

expect { expect(wojit.do_thing).to be true }.to change { wojit.value }

但这很奇怪。我错过了一些更惯用的东西吗?

答案

另一种方法是将返回值粘贴在变量中。

return_value = nil
expect{ return_value = wojit.do_thing }.to change{ wojit.value }
expect( return_value ).to be true

YMMV是否比嵌套的qazxswpo更好或更差。

另一答案

你可以为这个特定情况实现自己的自定义expect,如:

Matcher

然后你的期望就像是

RSpec::Matchers.define :respond_with do |expected| 
  match do |actual|
    actual.call == expected
  end
  # allow the matcher to support block expectations
  supports_block_expectations
  # make sure this executes in the correct context
  def expects_call_stack_jump?
    true
  end
end

这里的关键是it "Updates myvalue if condition is met" do wojit = MyKlass.new expect{wojit.do_thing}.to change(wojit, :value).and(respond_with(true)) end be等不支持块期望,因此不能与eq结合使用,所以我们实现了一个支持块期望(expect{...})并将其跳到堆栈上的等式匹配器(这非常重要)在这种情况下,否则更改块会创建一个冲突的实际*不确定我100%理解为什么,但相信我它确实)。

在这种情况下,supports_block_expectations? #=> true将是块体(作为actual),所以我们只需要调用它来将结果与预期值进行比较。

然而,你可以进一步抽象出类似的东西

Proc

这将允许您使用所有不支持块期望的方法,如此

RSpec::Matchers.define :have_response do |expectation| 

  supports_block_expectations

  def expects_call_stack_jump?
    true
  end
  #Actual matching logic 
  match do |actual|
     @actual_value = actual.respond_to?(:call) ? actual.call : actual
    expect(@actual_value).to(expectation)
  end

  failure_message do |actual|
    "expected response to be #{expectation.expected} but response was #{@actual_value}"
  end
  failure_message_when_negated do |actual|
    "expected response not to be #{expectation.expected} but response was #{@actual_value}"
  end

end

#define negation for chaining purposes as needed
RSpec::Matchers.define_negated_matcher :not_have_response, :have_response

只讨论这些方法中的任何一种方法是,块将被调用一次用于更改,一次用于响应检查,因此根据您的情况,这可能会导致问题。

另一答案

也许不是你想要的,但我实际上认为“更惯用的东西”是使用it "Updates myvalue if condition is met" do wojit = MyKlass.new expect{wojit.do_thing}.to change(wojit, :value).and(have_response(be true)) # or # expect{wojit.do_thing}.to not_have_response(be false).and(change(wojit, :value)) end describe块进行测试,以更好地表达你测试相同的情况。

context

以上是关于您能同时在RSpec中测试状态变化和返回值吗?的主要内容,如果未能解决你的问题,请参考以下文章

RSpec stubbed方法可以按顺序返回不同的值吗?

在某些 RSpec rails 请求示例中测试 HTTP 状态代码,但在其他示例中测试引发异常

RSpec2 错误代码测试

rspec 测试时,ActionMailer 方法调用在模块中返回 nil

RSpec:如何测试一个方法是不是被调用?

为啥在测试 RSPEC 时 JBuilder 不返回 JSON 中的响应正文