我如何在 minitest 中模拟一个块?
How can I mock with a block in 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
我怎样才能 mock/stub/something-else 才能让 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
中收到一个变量,那就是 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(又名 github 上的@zenspider / Twitter 上的@the_zenspider)给出了一个快速的答复和邀请将问题提交给 MiniTest github 问题跟踪器。
I did so,并从 Ryan 和 Pete Higgins(github 上的@phiggins)那里得到了一些很好的回应,我在这里完整地复制了这些回应。感谢两位的帮助!
@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.
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
Then testing is a simple matter:
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.
我试图对产生块的东西进行存根,但找不到答案。 (不确定这是否正是您要问的。)这是操作方法。
这是我们要模拟的 class:
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
很简单。这花了我一段时间才弄清楚。希望这对某人有所帮助:)
希望对 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
我怎样才能 mock/stub/something-else 才能让 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
中收到一个变量,那就是 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(又名 github 上的@zenspider / Twitter 上的@the_zenspider)给出了一个快速的答复和邀请将问题提交给 MiniTest github 问题跟踪器。
I did so,并从 Ryan 和 Pete Higgins(github 上的@phiggins)那里得到了一些很好的回应,我在这里完整地复制了这些回应。感谢两位的帮助!
@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.
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
Then testing is a simple matter:
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.
我试图对产生块的东西进行存根,但找不到答案。 (不确定这是否正是您要问的。)这是操作方法。
这是我们要模拟的 class:
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
很简单。这花了我一段时间才弄清楚。希望这对某人有所帮助:)