From: Brian Candler Date: 2010-08-02T22:53:35+09:00 Subject: Re: Mocking a method with a block Fernando Guillen wrote: > Is this possible?.. am I completely lost?.. is there any better way to > do this? I'd suggest you use an existing mocking library like "mocha". Example: -------- 8< ---------------- require 'net/pop' require 'test/unit' require 'rubygems' require 'mocha' # code to test class Foo attr_reader :opts def initialize(opts) @opts = opts end def bar(block) Net::POP3.start( opts[:server], opts[:port], opts[:user], opts[:pass] ) do |pop| pop.each_mail do |m| block.call( m ) end end end end # tests class MyTest < Test::Unit::TestCase def test_1 mails = ["xxxxx","yyy"] mockpop = mock mockpop.expects(:each_mail).multiple_yields(*mails) Net::POP3.expects(:start).with("127.0.0.1", 110, "a", "b").yields(mockpop) foo = Foo.new(:server=>"127.0.0.1", :port=>110, :user=>"a", :pass=>"b") res = [] foo.bar(lambda { |x| res << x.size }) assert_equal [5,3], res end # more expectation-based style def test_2 mails = ["xxxxx","yyy"] mockpop = mock mockpop.expects(:each_mail).multiple_yields(*mails) Net::POP3.expects(:start).with("127.0.0.1", 110, "a", "b").yields(mockpop) mockblock = mock seq = sequence(:block) mockblock.expects(:call).with(mails[0]).in_sequence(seq) mockblock.expects(:call).with(mails[1]).in_sequence(seq) foo = Foo.new(:server=>"127.0.0.1", :port=>110, :user=>"a", :pass=>"b") foo.bar(mockblock) end end -------- 8< ---------------- I find there's something unsatisfying about tests which look like this. Sometimes you can spend more effort on the mechanics of mocking than on solving the original problem, and the resulting tests are closely coupled to the internal implementation of your function. But that *is* what you asked for :-) Refactoring might make your code easier to test. e.g. -------- 8< ---------------- require 'net/pop' require 'test/unit' require 'rubygems' require 'mocha' class Foo attr_reader :opts def initialize(opts) @opts = opts end def bar(block) for_all_messages do |m| block.call(m) end end private def for_all_messages(&blk) Net::POP3.start( opts[:server], opts[:port], opts[:user], opts[:pass] ) do |pop| pop.each_mail(&blk) end end end class MyTest < Test::Unit::TestCase def test_1 foo = Foo.new({}) mails = ["xxxxx","yyy"] foo.expects(:for_all_messages).multiple_yields(*mails) res = [] foo.bar(lambda { |x| res << x.size }) assert_equal [5,3], res end end -------- 8< ---------------- HTH, Brian. -- Posted via http://www.ruby-forum.com/.