我如何用minitest中的块来模拟?

希望MiniTest人员的一个简单问题..

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

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 

我如何模拟/存根/其他东西,以便我可以通过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收到一个变量,那就是mock放置它的地方,而不是|message|

 def pick_first receiver = @poller.poll(idle_timeout: 60) do |message| process_msg(message) if some_condition(message) end puts receiver # this shows my 'message' !!! WHYYYY?? end 

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

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

我这样做了 ,得到了一些很好的回应,来自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 

有更短/“ruby”的方法来做到这一点,但它们与上面的相同,上面说明了更好的观点。