1

在一个方法中,我多次调用具有不同参数的另一个方法。我只想测试一个特定的调用以查看该调用的参数是否匹配某些条件。有没有比打断其他电话更好的方法呢?

例如,我有

def some_method
  foo(1)
  foo('a')
  foo(bar) if ... # some complex logic
  foo(:x)
  ...
end

我只想测试是否foo真的用 argument 调用bar

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

foo但是里面的其他调用怎么办some_method呢?

4

1 回答 1

1

好的,因此根据您想要观察到的最新评论,您正在记录一些输出。这是一个示例,您通过将 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 

您还可以升级您的简单puts以使用实际的 Logger 对象。然后,您可以在一个地方测试您的记录器与 STDOUT、STDERR 或其他任何地方一起使用,并且您可以在您关心记录的所有对象中测试它正在适当地记录到infodebug等。

您也可以从其他几个方向着手,但如果不了解您在做什么,这个潜在的答案可能已经足够长了。

希望这可以让您了解如何通过观察行为而不是依赖内部实现细节来解决此问题(例如,您使用的是puts自身而不是print "bar\n"另一种将文本输出到 IO 对象的方法。

于 2013-07-18T14:19:13.963 回答