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

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

Controller/UUID interaction.
Absolutely true.

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

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 W.