如何在minitest中模拟一个块? [英] How can I mock with a block in minitest?

查看:114
本文介绍了如何在minitest中模拟一个块?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

希望对MiniTest员工来说是一个简单的问题.

Hopefully a simple question for MiniTest folks..

我有一段代码,我将在此处浓缩为一个示例:

I have a section of code which I'll condense into an example here:

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()处理,从而馈入message?

How can I mock/stub/something-else so that I can feed a message through to be tested by the some_condition() and possibly processed with process_msg()?

即我要测试@poller.poll(idle_timeout: 60) do |message|.

我试图用模拟轮询器对Aws::SQS::QueuePoller#new进行存根处理,但是它并没有将消息返回给|message|.

I have tried to stub the Aws::SQS::QueuePoller#new with a mock poller, but it's not yielding the message to |message| just returning it..

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

This is what I have, which is not working:

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|中:

If I receive a variable in #pick_first, that's where the mock puts it, not into |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

推荐答案

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

Answering my own question, in case someone else has the same question.

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

I asked for help on this via Twitter, and the author of MiniTest, Ryan Davis (aka @zenspider on github / @the_zenspider on Twitter) gave a quick answer along with an invite to submit the question to the MiniTest github issue tracker.

我这样做了,并得到了瑞安(Ryan)和来自Pete Higgins(在github上为@phiggins),我在此全文进行复制.谢谢你们俩的帮助!

I did so, and got a couple of great responses, from Ryan and also from Pete Higgins (@phiggins on github), which I reproduce here in their entirety. Thank you to both of you for your help!

@phiggins 说:

那又怎么样呢?

What about something like:

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 说:

注意:我是反嘲讽的.我在这件事上几乎是反对.恕我直言,如果 您不能在不进行模拟的情况下测试某些东西,您可能有一个 设计问题.根据下面的文字进行相应的校准.

NOTE: I'm anti-mock. I'm almost anti-stub for that matter. IMHO, if you can't test something without mocking it, you probably have a design issue. Calibrate accordingly against the text below.

我建议使用Liskov替代校长(LSP),因为我当时 专注于测试process_msg在其中所做的正确的事情 语境.这个想法很简单,子类化,覆盖了方法中的 问题,并在测试中使用子类. LSP说测试 子类等效于测试超类.

I suggested using Liskov Substitution Principal (LSP) because I was focused on testing that process_msg did the right thing in that context. The idea is simple, subclass, override the method in question, and use the subclass within the tests. LSP says that testing a subclass is equivalent to testing the superclass.

对于轮询对象,您有三个问题(轮询, 过滤和处理)以这种方式进行,您可以选择其中一种 不应该进行测试(因为它是第三方代码).我会重构为 像这样的东西:

In the case of the polling object, you have three concerns (polling, filtering, and processing) going on in that method, one of whom you shouldn't be testing (because it is third-party code). I'd refactor to something like this:

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

有更短/更鲁比"的方式来做到这一点,但它们等同于上面的方法,并且上面的方法更好地说明了这一点.

There are shorter/"rubyier" ways to do this, but they're equivalent to the above and the above illustrates the point better.

这篇关于如何在minitest中模拟一个块?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆