ege*_*and 4 ruby tdd unit-testing 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
Run Code Online (Sandbox Code Playgroud)
我该如何模拟/存根/其他方式,以便我可以提供一个message通行证以供测试,some_condition()并可能通过处理process_msg()?
即我要测试@poller.poll(idle_timeout: 60) do |message|。
我试图Aws::SQS::QueuePoller#new用模拟轮询器对Stub进行存根,但是它不会产生|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
Run Code Online (Sandbox Code Playgroud)
如果我在中收到一个变量#pick_first,那就是模拟将其放入的地方,而不是|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
Run Code Online (Sandbox Code Playgroud)
如果其他人有相同的问题,请回答我自己的问题。
我通过Twitter寻求帮助,MiniTest的作者Ryan Davis(又名github上的@zenspider / Twitter上的@the_zenspider)给出了快速解答,并邀请将问题提交给MiniTest github问题跟踪器。
我这样做了,得到了瑞安(Ryan)和皮特·希金斯(Pete Higgins)(github上的@phiggins)的好评,我在此全文进行了全文复制。谢谢你们俩的帮助!
怎么样呢?
Run Code Online (Sandbox Code Playgroud)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
注意:我是反嘲笑的。我在这件事上几乎是反对。恕我直言,如果您不能在不进行模拟的情况下进行测试,则可能是设计问题。根据下面的文本进行相应的校准。
我建议使用Liskov替代负责人(LSP),因为我专注于测试process_msg在这种情况下是否正确。这个想法很简单,是子类,可以覆盖有问题的方法,并在测试中使用子类。LSP表示测试子类等同于测试超类。
就轮询对象而言,该方法涉及三个方面(轮询,过滤和处理),您不应该测试其中一个(因为它是第三方代码)。我会重构为这样的东西:
Run Code Online (Sandbox Code Playgroud)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然后测试很简单:
Run Code Online (Sandbox Code Playgroud)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有更短/更“鲁比”的方式来做到这一点,但是它们等同于上面的方法,并且上面的方法更好地说明了这一点。