Skip to content
has207 edited this page Dec 15, 2010 · 76 revisions

DOCUMENTATION

Definitions

Stub: fake object that returns a canned value

Mock: fake object that returns a canned value and has an expectation, i.e. it includes a built-in assertion

Spy: watches method calls and records/verifies if the method is called with required parameters

Distinguishing Features

FlexMock's automatic test runner integration allows you to write test helper methods that define your expectations and reuse them in different tests, possibly with different values. This is either completely impossible or very awkward to accomplish with other Python mocking libraries.

For example:

        def _mock_server_connection(url, value):
          flexmock(ServerConnection).should_receive('get').with_args(url).and_return(value).once

        class TestStuff(unittest.TestCase):
          def test_connection_to_server_a(self):
            _mock_server_connection('http://server-a/status', 'OK')
            run_the_code_that_will_try_connection_to_server_a()

          def test_connection_to_server_b(self):
            _mock_server_connection('http://server-b/status', 'BAD')
            run_the_code_that_will_try_connection_to_server_b()

It's easy to write helper methods that define stubs in this way using almost any library out there. However, what's interesting above the above is the "once" modifier on the expectation. In addition to providing stub values for the specified method the _mock_server_connection() helper method also guarantees that the call to that method will be made with the specified parameters. That logic is completely taken care of by calling the helper method, freeing you from having to make any extra explicit assertions later in your tests.

FlexMock also supports a much terser and cleaner syntax than most Python mocking libraries out there. As a result, you can focus on what to test rather than trying to figure out how to coax the mocking library into doing what you need.

SAMPLE USAGE

        import unittest
        from flexmock import flexmock

        class SomeTest(unittest.TestCase):
          def test_something(self):
             ...

Make a mock object

        mock = flexmock(method1=lambda: 'a', attr='b')
        mock.should_receive('method2').and_return('c')
        mock.should_receive('method3').and_raise(Exception)
        mock.attr
        >> 'b'
        mock.method1()
        >> 'a'
        mock.method2()
        >> 'c'
        mock.method3()
        >> Traceback (most recent call last):
        >>   File "<stdin>", line 1, in <module>
        >>   File "flexmock.py", line 336, in mock_method
        >>     raise expectation.exception
        >> Exception

Add some automatically checked expectations

        mock = flexmock(some_object)
        mock.should_receive('method_bar').with_args('a').once
        mock.should_receive('method_bar').with_args('b').at_least.twice
        mock.should_receive('method_bar').with_args('c').at_most.once
        mock.should_receive('method_bar').with_args('d').never

If any of these are not called the correct number of times an exception will be raised.

Add a spy (or proxy) to a method

-- matching specific arguments --

        flexmock(some_object).should_receive('method_bar').with_args(arg1, arg2).at_least.once.and_execute

-- matching any arguments --

        flexmock(some_object).should_receive('method_bar').twice.and_execute

The real method_bar() will be called, but flexmock will verify that it's called the correct number of times and with the expected arguments.

Stub out a method on an existing object

        class User: pass
        user = User()
        flexmock(user).should_receive('get_name').and_return('name')

Stub out a method for all instances of a class

        class User: pass
        flexmock(User)
        User.should_receive('method_foo').and_return('value_bar')
        user = User()
        user.method_foo()
        >> 'value_bar'

Make a partial mock based on class instance

        class Group: pass
        group = Group()
        flexmock(group)
        # stub -- no assertions checked
        group.should_receive('get_name').and_return('group_name')
        # mock -- make an assertion that the method will get called one time with any args 
        group.should_receive('get_member').and_return([]).once

Return different values on successive method invokations

        group.should_receive('get_member').and_return('user1').and_return('user2').and_return('user3')
        group.get_member()
        >> 'user1'
        group.get_member()
        >> 'user2'
        group.get_member()
        >> 'user3'

-- or use the short-hand form --

        group.should_receive('get_member').and_return('user1', 'user2', 'user3').one_by_one

-- you can also mix return values with exception raises --

        group.should_receive('get_member').and_return('user1').and_raise(Exception).and_return('user2')

Override "__new__" method on a class and return fake instances

        class Group(object): pass
        flexmock(Group, new_instances='foo')

Overriding new instances of old-style classes is currently not supported directly, you should make the class inherit from "object" in your code first. Luckily, multiple inheritance should make this pretty painless.

Create a mock generator

        flexmock(foo).should_receive('gen').and_yield(1, 2, 3)
        for i in foo.gen():
          print i
        >> 1
        >> 2
        >> 3

Test runner integration

unittest.TestCase

        from flexmock import flexmock

Other test runners

As far as I can tell most test runners out there support unittest.TestCase so as long as your tests are in a class that inherits from unittest.TestCase flexmock will just work.

If you must have tests at module level or require hooking into more fine-grained setup/teardown functionality, you can check the flexmock_unittest() function to see how to implement integration with another test runner. It should be reasonably straight-forward -- you need to define flexmock_<test_runner>() that contains a class that inherits from FlexMock and implements update_teardown(). Something along the lines of:

        def flexmock_<test_runner>(*kargs, **kwargs):
          import <test_runner>
          class UnittestFlexMock(FlexMock):
            def update_teardown(self, test_runner=<test_runner>, teardown_method='tearDown'):
              FlexMock.update_teardown(self, test_runner, teardown_method)
          return UnittestFlexMock(*kargs, **kwargs)

-- then in your test code --

        from flexmock import flexmock_<test_runner> as flexmock

And don't forget to send me the patch! :)

EXPECTATION MATCHING

Creating an expectation with no arguments will by default match all arguments, including no arguments.

-- that is --

        flexmock(foo).should_receive('method_bar').and_return('bar')

-- will be matched by --

       foo.method_bar()
       >> 'bar'
       foo.method_bar('foo')
       >> 'bar'
       foo.method_bar('foo', 'bar')
       >> 'bar'

In addition to exact values, you can also match against the type or class of the argument.

-- so --

        flexmock(foo).should_receive('method_bar').with_args(object)

-- will match any single argument --

        flexmock(foo).should_receive('method_bar').with_args(str)

-- will match any single string argument --

        flexmock(foo).should_receive('method_bar').with_args(int, object, 'foo')

-- will match any set of three arguments where the first one is an integer, second one is anything, and third is string 'foo' --

Matching against user defined classes is also supported in the same fashion.

You can also override the default match with another expectation for the same method.

-- that is --

        flexmock(foo).should_receive('method_bar').and_return('bar')
        flexmock(foo).should_receive('method_bar').with_args('foo').and_return('foo')

-- so now --

        foo.method_bar()
        >> 'bar'
        foo.method_bar('foo', 'bar')
        >> 'bar'

-- but --

       foo.method_bar('foo')
       >> 'foo'

The order of the expectations being defined is significant, with later expectations having higher precedence than previous ones. Which means that if you reversed the order of the example expectations above the more specific expectation would never be matched.

You can take advantage of this fact by applying the "ordered" keyword which will make an out of order call raise and exception:

        flexmock(foo).should_receive('method_bar').with_args('bar').and_return('bar').ordered
        flexmock(foo).should_receive('method_bar').with_args('foo').and_return('foo').ordered

-- so calling the methods in the same order will be fine --

       foo.method_bar('bar')
       >> 'bar'
       foo.method_bar('foo')
       >> 'foo'

-- but trying to call the second one first will result in an exception --

       foo.method_bar('foo')
       >> Traceback (most recent call last):
       >>   File "<stdin>", line 1, in <module>
       >>   File "flexmock.py", line 449, in mock_method
       >>     expectation = self._get_flexmock_expectation(method, arguments)
       >>   File "flexmock.py", line 415, in _get_flexmock_expectation
       >>     self._verify_call_order(e, args, name)
       >>   File "flexmock.py", line 426, in _verify_call_order
       >>   FlexMock._format_args(exp.method, exp.args)))
       >>     flexmock.MethodCalledOutOfOrder: method_bar("foo") called before method_bar("bar")
Clone this wiki locally