0

我正在尝试使用 RSpec。由于我不喜欢模拟,我想使​​用StringIO对象模拟控制台打印。

所以,我想测试Logger该类是否写入Welcome控制台。为此,我的想法是从规范文件中覆盖puts内部使用的方法Logger,以便在Logger其他地方使用时实际上没有任何变化。

这是一些代码:

describe Logger do
    Logger.class_eval do
        def puts(*args)
            ???.puts(*args)
        end
    end

    it 'says "Welcome"' do
end

这样做,我需要在类和测试类StringIO之间共享一些对象(它会去问号现在所在的位置) 。Logger

我发现当我在 RSpec 测试中时,selfClass. 我最初的想法是做这样的事情:

Class.class_eval do
    attr_accessor :my_io
    @my_io = StringIO.new
end

然后替换???Class.my_io.

当我这样做时,一千个钟声在我脑海中响起,告诉我这不是一种干净的方法。

我能做些什么?

PS:我还是不明白:

a = StringIO.new
a.print('a')
a.string # => "a"
a.read # => "" ??? WHY???
a.readlines # => [] ???

仍然:StringIO.new('hello').readlines # => ["hello"]

4

3 回答 3

1

为了回应您的最后一个问题,StringIO模拟文件行为。当您对其进行写入/打印时,输入光标位于您最后写入的内容之后。如果你写了一些东西并想读回来,你需要根据http://ruby-doc.org/stdlib-1.9.3/libdoc/stringio/rdoc/StringIOrewind重新定位自己(例如,使用、seek等) 。 html

相反,在 0 的位置离开时,将其StringIO.new('hello')设置hello为字符串的初始内容。无论如何,该string方法只返回内容,与位置无关。

于 2013-11-06T17:48:05.327 回答
1

目前尚不清楚为什么 RSpec 中的测试双重机制存在问题。

也就是说,您共享方法的方法有效,尽管:

  • selfRSpec 中的匿名类这一事实describe并不真正相关
  • Class您可以定义自己的类和关联的类方法并“共享”它们,而不是使用 的实例方法,如下所示:

    class Foo def self.bar(arg) puts(arg) end end

    描述“共享字符串”做

    Foo.class_eval 做 def self.puts(*args) MyStringIO.my_io.print(*args) end end

    类 MyStringIO @my_io = StringIO.new def self.my_io ; @my_io ; 结束结束

    它'说“欢迎”' do Foo.bar("Welcome") expect(MyStringIO.my_io.string).to eql "Welcome" end

    结尾

于 2013-11-06T19:16:09.393 回答
0

Logger已经允许在构造上指定输出设备,因此您可以轻松地StringIO直接传递,而无需重新定义任何内容:

require 'logger'

describe Logger do

  let(:my_io) { StringIO.new }
  let(:log)   { Logger.new(my_io) }

  it 'says welcome' do
    log.error('Welcome')
    expect(my_io.string).to include('ERROR -- : Welcome')
  end
end

正如其他海报所提到的,尚不清楚您是打算测试Logger还是使用它的一些代码。在后者的情况下,考虑记录器注入到客户端代码中。

这个 SO 问题的答案还显示了几种Logger在客户端之间共享共同点的方法。

于 2013-11-06T20:16:52.690 回答