Need help with FlexMock (or mocking in general)

I'm trying to mock a single method call in a controller I'm testing (this isn't a Rails question). I only want to mock the method for certain tests while the "real" method continues to function as before in the remainder of the tests. I'm trying to figure out how to use FlexMock to do this but I'm coming up empty.

Here's some example code showing what I'm trying to do, and what I'd like to mock.

require 'rubygems'
require 'uuid'

class StorageController
   def get_next_uuid
     UUID.new
   end

   def assign_uuid
     3.times do |attempt|
       uuid = get_next_uuid
       return [uuid] if not @records.include?(uuid)
     end
    ["Too many uuid collisions"]
   end
end

And now for the tests...

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

   def test_assign_uuid
     # this call should use the real #get_next_uuid or real #UUID.new methods
     result = @controller.assign_uuid
     assert_equal 0, result[0] =~ /^([a-z\d]+)\-([a-z\d]+)\-([a-z\d]+)\-([a-z\d]+)\-([a-z\d]+)$/

     # want to mock #get_next_uuid or #UUID.new here! Want to mock it out so I can force
     # the #assign_uuid method to "fail" and returns the error message
     result = @controller.assign_uuid
     assert_equal 0, result[0] =~ /^Too many uuid collisions$/
   end
end

So how do I mock out either #get_next_uuid or UUID.new? I thought mocking UUID.new would be easiest but I can't see how to use FlexMock to handle the case where there are no instance methods and only class methos (that is, you don't call UUID.new to get an instance of the UUID class, it just returns a value directly).

I asked a similar question on the Rails list but it occurred to me this is primarily a question about mocks. I'd like to get better at creating and using them within my tests.

Thanks for the help!

cr

unknown wrote:

I'm trying to mock a single method call in a controller I'm testing
(this isn't a Rails question). I only want to mock the method for
certain tests while the "real" method continues to function as before
in the remainder of the tests. I'm trying to figure out how to use
FlexMock to do this but I'm coming up empty.

The first step is to decide what you are testing. Are you testing the
controller, or the UUID class? In this case, it looks like it is the
controller. In fact, it looks like you are specifically testing the
Controller/UUID interaction.

So, I would recommend something like this. First, write your controller
as you would normally do:

   class StorageController
     def get_next_uuid
       UUID.new
     end
     def assign_uuid
       # blah, blah, blah ... more code
     end
   end

Now, in your test file, cleverly modify the Storage controller class to
allow you to inject a mock UUID:

  class StorageController
    attr_accessor :test_uuid
    def get_next_uuid
      (@test_uuid || UUID).new
    end
  end

(There are several ways of doing this, this is just an ad hoc way that
is simple for one-off situations. If you find yourself doing this a
lot, then you might want to consider a dependency injection framework).

Now, write the tests. I would start off like this:

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

    def setup
      @controller = StorageController.new
      @controller.uuid_class = flexmock("uuid")
    end

  [...]

Notice the name of the test. We are not testing the entire
StorageController API, just the UUID interaction. That allows us to
have a setup that creates the controller and injects our mock UUID.

Now, the first test. Let's make sure the controller can handle assign a
simploe UUID.

    def test_controller_assigns_uuid
      @controller.uuid_class.
        should_receive(:new).once.and_return('a-a-a-a-a')
      assert_equal ['a-a-a-a-a'],
        @controller.assign_uuid
    end

We tell the mock it will be called once and will return a fixed value.
We then make sure the value is returned.

Ok, next test. Let's make sure the controller correctly handles UUID
collisions. We will setup our UUID to expect to be called 4 times (once
for the initial uuid, and then 3 times during collision retry loop).

    def test_controller_handles_excessive_uuid_collisions
      @controller.uuid_class.
        should_receive(:new).times(4).and_return('a-a-a-a-a')
      @controller.assign_uuid
      assert_equal ['Too many uuid collisions'],
        @controller.assign_uuid
    end

That covers the tests in your example. There are a couple more tests I
would suggest: (a) Mulitple UUID requests returning different values,
(b) cause a UUID colision, but not so many that the assign fails. But
that's enough for this message.

Does this help?

-- Jim Weirich

···

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

unknown wrote:

I'm trying to mock a single method call in a controller I'm testing
(this isn't a Rails question). I only want to mock the method for
certain tests while the "real" method continues to function as before
in the remainder of the tests. I'm trying to figure out how to use
FlexMock to do this but I'm coming up empty.

The first step is to decide what you are testing. Are you testing the
controller, or the UUID class? In this case, it looks like it is the
controller. In fact, it looks like you are specifically testing the
Controller/UUID interaction.

Absolutely true.

So, I would recommend something like this.
[snip]
Now, in your test file, cleverly modify the Storage controller class to
allow you to inject a mock UUID:

  class StorageController
    attr_accessor :test_uuid
    def get_next_uuid
      (@test_uuid || UUID).new
    end
  end

(There are several ways of doing this, this is just an ad hoc way that
is simple for one-off situations. If you find yourself doing this a
lot, then you might want to consider a dependency injection framework).

A dependency injection framework? Mmmm... getting a bit beyond my knowledge and experience. Though, come to think of it, I do recall running across a write-up of using a dependency injection framework in Rails instead of the existing Test::Unit stuff. I didn't read the whole paper... I should google for that again. I may need this capability somewhere down the road.

Now, write the tests. I would start off like this:

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

    def setup
      @controller = StorageController.new
      @controller.uuid_class = flexmock("uuid")
    end

This all worked. I had to change "@controller.uuid_class=" to "@controller.test_uuid=" to match the previous accessor setup, but otherwise this all worked right out of the box. Amazing stuff. Every day I learn something new about the dynamism of Ruby.

[snip]
That covers the tests in your example. There are a couple more tests I
would suggest: (a) Mulitple UUID requests returning different values,
(b) cause a UUID colision, but not so many that the assign fails. But
that's enough for this message.

Now that I have the base tests working, I will do as you suggest.

Does this help?

Does it ever! When I posted this message I purposefully put "FlexMock" into the title in the hope that you (as the author of it) would notice and answer the question. Thanks very much for doing so!

The Flexmock documentation examples didn't cover the case where a class mocked just a single method which is why I was floundering a bit here. After I experiment a bit more and wrap my head around how this is working, I hope you'll accept a documentation patch that illustrates this.

cr

···

On Jun 16, 2006, at 9:31 PM, Jim Weirich wrote: