我如何用最小的块来模拟?

时间:2015-05-07 02:23:50

标签: ruby unit-testing tdd minitest

希望MiniTest人员能够提出一个简单的问题..

我有一段代码,我将在这里压缩成一个例子:

import csv
Mysum = 0
input_attribute = input("Enter attribute for comparison (lowercase) :")
input_SetNum = input("How many data sets are you comparing: ")
print ("You entered " + input_SetNum) 

data_sets = {}
for i in range(1, int(input_SetNum)+1):
  with open(input("Please enter the file name with .csv: ")) as csvfile:
      reader = csv.DictReader(csvfile)
      for row in reader:  # The code in your `for` block needs to be indented or you're going to get more errors...
          data_sets[i] = (row[input_attribute])
          print (data_sets[i])


Mysum = sum(data_sets[i])
print(Mysum)

我如何模拟/存根/其他东西,以便我可以提供class Foo def initialize(name) @sqs = Aws::SQS::Client.new @id = @sqs.create_queue( queue_name: name ).fetch(:queue_url) @poller = Aws::SQS::QueuePoller.new(@id) end def pick_first @poller.poll(idle_timeout: 60) do |message| process_msg(message) if some_condition(message) end end 以通过message进行测试,并可能使用some_condition()进行处理?

即。我想测试process_msg()

我曾尝试使用模拟轮询器来存储@poller.poll(idle_timeout: 60) do |message|,但它不会将消息传递给Aws::SQS::QueuePoller#new只是返回它..

这就是我所拥有的,正在工作:

|message|

如果我在mockqueue = MiniTest::Mock.new mocksqs = MiniTest::Mock.new mocksqs.expect :create_queue, mockqueue, [Hash] mockpoller = MiniTest::Mock.new mockpoller.expect :poll, 'message', [{ idle_timeout: 60 }] Aws::SQS::Client.stub :new, mocksqs do Aws::SQS::QueuePoller.stub :new, mockpoller do queue = Foo.new(opts) queue.pick_first end end 收到变量,那就是模拟放置的变量,而不是#pick_first

|message|

2 个答案:

答案 0 :(得分:5)

回答我自己的问题,以防其他人有同样的问题。

我通过Twitter寻求帮助,MiniTest的作者,Ryan Davis(又名@zenpider on github / @the_zenspider on Twitter)给出了快速回答以及邀请将问题提交给MiniTest github问题跟踪器

I did so,得到了一些很好的回应,来自Ryan和Pete Higgins(@phiggins on github),我在这里完整地再现。谢谢你们两位的帮助!

@phiggins说:

  

如下:

class Foo   def initialize(name, opts={})
  @sqs    = Aws::SQS::Client.new
  @id     = @sqs.create_queue( queue_name: name ).fetch(:queue_url)
  @poller = opts.fetch(:poller) { Aws::SQS::QueuePoller.new(@id) }   end

  def pick_first
    @poller.poll(idle_timeout: 60) do |message|
    process_msg(message) if some_condition(message)
    end
  end
end

# later, in your tests
describe Foo do
  it "does the thing in the block" do
  # could be moved into top-level TestPoller, or into shared setup, etc.
  poller = Object.new
  def poller.poll(*) ; yield ; end

  foo = Foo.new("lol", :poller => poller)
  foo.pick_first

  assert foo.some_state_was_updated
  end
end

@zenspider说:

  

注意:我是反模拟的。对于这个问题我几乎是反对的。恕我直言,如果   你不能在没有嘲笑的情况下测试一些东西,你可能有一个   设计问题。根据以下文字进行相应校准。

     

我建议使用Liskov Substitution Principal(LSP),因为我是   专注于测试process_msg做了正确的事情   上下文。这个想法很简单,子类,覆盖了方法   问题,并在测试中使用子类。 LSP说测试   子类相当于测试超类。

     

对于投票对象,您有三个问题(民意调查,   过滤和处理)在那个方法中进行,其中一个你   不应该测试(因为它是第三方代码)。我会重构   像这样的东西:

class Foo
  # ....

  def poll
    @poller.poll(idle_timeout: 60) do |message|
      yield message
    end
  end

  def pick_first
    poll do |message|
      process_msg(message) if some_condition(message)
    end
  end
end
     

然后测试是一件简单的事情:

class TestFoo1 < Foo
  def poll
    yield 42 # or whatever
  end

  # ...
end

# ...

assert_equal 42, TestFoo1.new.pick_first # some_condition truthy
assert_nil       TestFoo2.new.pick_first # some_condition falsey
     

有更短/“rubyier”的方法可以做到这一点,但它们与上面相同,上面说明了这一点。

答案 1 :(得分:0)

我试图对产生障碍的东西进行存根处理,但找不到答案。 (不确定这是否正是您的要求。)这是操作方法。

这是我们要模拟的课程:

class Foo
  def bar
    yield(42)
  end
end

在测试中,我们实例化了对象:

foo = Foo.new

然后,我们可以使用普通的Ruby覆盖此方法以执行其他操作:

def foo.bar
  yield(16)
end

现在,当我们调用它时,它会调用存根版本:

foo.bar do |value|
  puts value
  # => 16
end

足够容易。这花了我一段时间才能弄清楚。希望这对某人有帮助:)