Problem with FlexMock::TestCase#flexstub in 0.4.0

Jim,

I'm really glad to see the stubbing capabilities added to FlexMock.
However, I'm having a problem with the flexstub method.

I have several domain classes (ActiveRecord) in my application, and I
am working on functionality that will import data into the application
from a de-normalized tab-separated file we get from our customer on an
ongoing basis. Each line in the file represents a Product, so the code
calls Product.import( data_hash_from_line ) for each line in the file.
Product.import then calls the import method on several related
classes, giving each one the responsibility for knowing how to handle
its portion of the data.

For the unit tests, I'm using FlexMock to stub out the calls to import
in the other classes. If I run a single test case using `ruby
test/unit/my_test_file.rb`, everything works as expected. However,
when I try to run the whole test suite using `rake` (this is a Rails
app, BTW), I start to see a lot of errors like this:

16) Error:
test_should_return_status_if_it_exists(StatusTest::Import):
NoMethodError: undefined method `import' for #<FlexMock:0x236a0e0>
    /usr/local/lib/ruby/gems/1.8/gems/flexmock-0.4.0/lib/flexmock.rb:113:in
`method_missing'
    /usr/local/lib/ruby/gems/1.8/gems/flexmock-0.4.0/lib/flexmock.rb:228:in
`mock_wrap'
    /usr/local/lib/ruby/gems/1.8/gems/flexmock-0.4.0/lib/flexmock.rb:108:in
`method_missing'
    (eval):3:in `import'
    (eval):5:in `import'
    /Users/jwilger/projects/application/test/test_helper.rb:70:in `import'
    ./test/unit/status_test.rb:55:in `test_should_return_status_if_it_exists'

It looks like the FlexMock::TestCase#flexmock_teardown method is
removing the stub for Status.import, but it isn't restoring the
original method.

···

--
Regards,
John Wilger
http://johnwilger.com

-----------
Alice came to a fork in the road. "Which road do I take?" she asked.
"Where do you want to go?" responded the Cheshire cat.
"I don't know," Alice answered.
"Then," said the cat, "it doesn't matter."
- Lewis Carrol, Alice in Wonderland

Bad form replying to myself, I know -- but I think I figured out
what's going on...

In my tests, I've been been doing this:

  class MyTest < Test::Unit::TestCase
    include FlexMock::TestCase

    def setup
      # set up a default stub
      flexstub( SomeClass ).should_receive( :foo )
    end

    def test_method_that_calls_foo_but_not_interested_in_foo_a
      SomeOtherClass.do_something_that_calls_foo
      # other assertions, etc
    end

    def test_method_that_calls_foo_but_not_interested_in_foo_b
      SomeOtherClass.do_something_that_calls_foo
      # other assertions, etc
    end

    def test_method_that_calls_foo_and_cares
      flexstub( SomeClass ).should_receive( :foo ).once.with( :bar )
      SomeOtherClass.do_something_that_calls_foo
    end
  end

So, I want to be able to set up a default stub for SomeClass.foo, so
that I can test SomeOtherClass.do_something_that_calls_foo without
using the actual implimentation of SomeClass.foo, but I don't want to
have to write that default stub out in each test method.

The problem is that when test_method_that_calls_foo_and_cares is run,
it thinks the default stub is the _original_ method, and when the test
is finished running, it's the default stub method that ends up being
restored.

In order to make this work, we just need to make sure that the
original method isn't overwritten if we make more than one call to
#should_receive for a particular stub. It's a pretty simple change on
line 1008 of lib/flexmock.rb. Just change:

  @method_definitions[method_name] = @obj.method(method_name)

to:

  @method_definitions[method_name] ||= @obj.method(method_name)

All existing unit tests for the FlexMock library still pass with that
change in place (though I've not yet written any tests to cover this
case), _and_ it solves the problem I was having with FlexMock in my
own application.

···

On 9/5/06, John Wilger <johnwilger@gmail.com> wrote:

For the unit tests, I'm using FlexMock to stub out the calls to import
in the other classes. If I run a single test case using `ruby
test/unit/my_test_file.rb`, everything works as expected. However,
when I try to run the whole test suite using `rake` (this is a Rails
app, BTW), I start to see a lot of errors like this:

16) Error:
test_should_return_status_if_it_exists(StatusTest::Import):
NoMethodError: undefined method `import' for #<FlexMock:0x236a0e0>

It looks like the FlexMock::TestCase#flexmock_teardown method is
removing the stub for Status.import, but it isn't restoring the
original method.

--
Regards,
John Wilger
http://johnwilger.com

-----------
Alice came to a fork in the road. "Which road do I take?" she asked.
"Where do you want to go?" responded the Cheshire cat.
"I don't know," Alice answered.
"Then," said the cat, "it doesn't matter."
- Lewis Carrol, Alice in Wonderland