Ruby 如何在RSpec中只测试多个方法调用中的一个?

Ruby 如何在RSpec中只测试多个方法调用中的一个?,ruby,rspec,Ruby,Rspec,在一个方法中,我使用不同的参数多次调用另一个方法。我只想测试一个特定的调用,看看该调用的参数是否符合某些条件。有没有更好的方法比每隔一次打一次电话 例如,我有 def some_method foo(1) foo('a') foo(bar) if ... # some complex logic foo(:x) ... end 我只想测试foo是否实际使用参数bar调用 subject.should_receive(:foo).with(correct_value_of_b

在一个方法中,我使用不同的参数多次调用另一个方法。我只想测试一个特定的调用,看看该调用的参数是否符合某些条件。有没有更好的方法比每隔一次打一次电话

例如,我有

def some_method
  foo(1)
  foo('a')
  foo(bar) if ... # some complex logic
  foo(:x)
  ...
end
我只想测试
foo
是否实际使用参数
bar
调用

subject.should_receive(:foo).with(correct_value_of_bar)

但是如何处理同一
some\u方法中的
foo
的其他调用呢?

好的,根据您最近的评论,您希望看到您正在记录一些输出。下面是一个示例,通过将STDOUT替换为StringIO实例,您可以观察到这种行为:

# foo.rb
require 'rubygems'
require 'rspec'
require 'rspec/autorun'

require 'stringio'

class Foo
  def something
    puts 1
    puts 'a'
    puts 'bar' if true # some complex logic
    puts :x
  end
end


describe Foo do
  describe '#something' do
    context "and something complex happens" do
      let(:io){ StringIO.new }
      it "logs to STDOUT" do
        $stdout = io
        Foo.new.something
        expect(io.tap(&:rewind).read).to include("bar\n")
      end
    end
  end
end
这会起作用,但这样做的副作用远远超出了您的具体示例,因为我们正在更改全局
$stdout
。这可以通过使用穷人的依赖注入和构造函数默认值来改进:

class Foo
  def initialize(io=STDOUT)
    @io = io
  end

  def something
    puts 1
    puts 'a'
    puts 'bar' if true # some complex logic
    puts :x
  end

  protected

  def puts(*args)
    @io.puts *args
  end
end


describe Foo do
  describe '#something' do
    context "and something complex happens" do
      let(:io){ StringIO.new }
      it "logs to STDOUT" do
        Foo.new(io).something
        expect(io.tap(&:rewind).read).to include("bar\n")
      end
    end
  end
end
在上面的例子中,我们给自己传递我们将要放入的IO对象的能力。这使我们能够观察到行为,而不会产生超出测试范围的副作用,并且以一种让我们正在测试的对象保持真实的方式(即:我们不会像以前那样修改对象本身,但现在已经删除了关于使用
as_null_object
建议的注释)

您还可以在构造函数上使用选项散列,并将惰性赋值推送到
initialize
本身:

def initialize(arg1, arg2, options={})
   @io = options[:io] || STDOUT
end 
您还可以升级简单的
put
,以使用实际的记录器对象。然后,您可以在一个地方测试您的记录器与STDOUT、STDERR或其他任何地方一起工作,并且您可以在您关心日志记录的所有对象中测试它是否正确地记录到
info
debug
,等等

你也可以从多个方面来考虑这个问题,但是如果不知道你在做什么,这个潜在的答案可能已经足够长了


希望这能为您提供一些想法,让您了解如何通过观察行为而不是依赖内部实现细节来实现这一点(例如,您使用的是
put
本身,而不是
print“bar\n”
或将文本输出到IO对象的另一种方法。

好的,根据您最近的评论,您希望观察到您正在记录一些输出。下面是一个示例,通过将STDOUT替换为StringIO实例来观察此行为:

# foo.rb
require 'rubygems'
require 'rspec'
require 'rspec/autorun'

require 'stringio'

class Foo
  def something
    puts 1
    puts 'a'
    puts 'bar' if true # some complex logic
    puts :x
  end
end


describe Foo do
  describe '#something' do
    context "and something complex happens" do
      let(:io){ StringIO.new }
      it "logs to STDOUT" do
        $stdout = io
        Foo.new.something
        expect(io.tap(&:rewind).read).to include("bar\n")
      end
    end
  end
end
这是可行的,但这样做的副作用远远超出了您的具体示例,因为我们正在更改全局
$stdout
。使用穷人的依赖项注入和构造函数默认值可以改善这一点:

class Foo
  def initialize(io=STDOUT)
    @io = io
  end

  def something
    puts 1
    puts 'a'
    puts 'bar' if true # some complex logic
    puts :x
  end

  protected

  def puts(*args)
    @io.puts *args
  end
end


describe Foo do
  describe '#something' do
    context "and something complex happens" do
      let(:io){ StringIO.new }
      it "logs to STDOUT" do
        Foo.new(io).something
        expect(io.tap(&:rewind).read).to include("bar\n")
      end
    end
  end
end
在上面的例子中,我们让自己能够传入我们将要放入的IO对象。这让我们能够观察行为,而不会产生超出测试范围的副作用,并且以一种让我们正在测试的对象保持真实的方式(即:我们没有像前面那样修改对象本身,但现在删除了关于使用
作为\u null\u对象
建议的注释)

您还可以在构造函数上使用选项散列,并将惰性赋值推送到
initialize
本身:

def initialize(arg1, arg2, options={})
   @io = options[:io] || STDOUT
end 
您还可以升级简单的
put
以使用实际的记录器对象。然后您可以在一个地方测试记录器是否与STDOUT、STDERR或其他任何地方一起工作,并且您可以在您关心日志记录的所有对象中测试它是否正确地记录到
info
debug
,等等

你也可以从多个方面来考虑这个问题,但是如果不知道你在做什么,这个潜在的答案可能已经足够长了


希望这能为您提供一些想法,让您了解如何通过观察行为而不是依赖内部实现细节来实现这一点(例如,您使用的是
put
本身,而不是
print“bar\n”
或将文本输出到IO对象的另一种方法。

由于此调用,
某些方法
表现出什么行为?为什么您关心对
foo
的一个特定调用?例如,假设foo是
put
,所以我打印多行文本。首先,
放置“一些标题”
放置“一些头球"
,然后是实际内容
put bar
。我想测试是否打印了
bar
的正确值,但是如何忽略静态文本的其他
put
?祖浩,你看到我贴出的答案了吗?有用吗?@ZachDennis感谢你的详细解释。虽然我使用了
put
作为示例,但是so不幸的是,我不能像你在实际案例中建议的那样使用
StringIO
。但是你的回答确实让我大开眼界。谢谢!因为这个调用,
一些方法
表现出了什么行为?你为什么关心对
foo
的一个特定调用?例如,让我们假设foo是
put
,所以我正在打印mu多行文本。首先,
放“一些标题”
放“一些标题”
,然后是实际内容
put bar
。我想测试是否打印了
bar
的正确值,但是如何忽略静态文本的其他
put
?祖浩,你看到我贴出的答案了吗?有用吗?@ZachDennis感谢你的详细解释。虽然我使用了
put
作为示例,但是so不幸的是,我不能像您在实际案例中建议的那样使用
StringIO
。但您的回答确实让我大开眼界。谢谢!