From: Brian Candler on
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/.

From: Fernando Guillen on
Brian Candler wrote:
> 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".


This was how I started to try mock the Net::POP3.

But instead of using the .yields method, that I didn't know about, I
tried to mock directly the Net::POP3.start method and this didn't work.

Thanks a lot for your comprehensive examples and also for the
re-factoring suggestion for an easier mock.

Best regards.

f.
--
Posted via http://www.ruby-forum.com/.

From: Brian Candler on
Fernando Guillen wrote:
> But instead of using the .yields method, that I didn't know about, I
> tried to mock directly the Net::POP3.start method and this didn't work.

It would be really useful if you could attach arbitrary behaviour to a
mocked method - perhaps 'returns' with a block:

m = [1,2,3]
Net::POP3.expects(:start).returns { m.each { puts m }; m.size }

This is something I've missed badly in the past, and I've ended up
mocking directly using Object.new and defining singleton methods.
--
Posted via http://www.ruby-forum.com/.

From: Fernando Guillen on
Hi again people,

Following your suggestions I am trying to build an small library to
abstract this behavior on a reusable way:

* http://github.com/fguillen/NetPopMock

My problem now is that I'm not able to use the _raw_mails_ array sent on
the **NetPopMock.fake** call

See the test example:

*
http://github.com/fguillen/NetPopMock/blob/master/test/net_pop_mock_test.rb#L15

Into the mocked class:

* http://github.com/fguillen/NetPopMock/blob/master/net_pop_mock.rb#L30

Jesus was the first one suggesting me to use _class_eval_ and I told him
that I thought I was enough information to work on my self.. well that
was not true.. I need help again :)

I know the solution is in a mix of _eval_ and _binding_ but I don't find
it.

Thanks again

f.
--
Posted via http://www.ruby-forum.com/.

From: Fernando Guillen on
Thinking I found the solution:

*
http://github.com/fguillen/NetPopMock/commit/7003aa7db33f98165a10987058ece171a7b371dd

Do you think is a clean solution?

Regards

f.
--
Posted via http://www.ruby-forum.com/.