<commit_before>=================================
 Chai - Python Mocking Made Easy
=================================

:Version: 0.1.4
:Download: http://pypi.python.org/pypi/chai
:Source: https://github.com/agoragames/chai
:Keywords: python, mocking, testing, unittest, unittest2

.. contents::
    :local:

.. _chai-overview:

Overview
========

Chai provides a very easy to use api for mocking/stubbing your python objects, patterned after the `Mocha <http://mocha.rubyforge.org/>`_ library for Ruby.

.. _chai-example:

Example
=======

The following is an example of a simple test case which is mocking out a get method
on the ``CustomObject``. The Chai api allows use of call chains to make the code 
short, clean, and very readable. It also does away with the standard setup-and-replay
work flow, giving you more flexibility in how you write your cases. ::


    from chai import Chai

    class CustomObject (object): 
        def get(self, arg):
            pass

    class TestCase(Chai):
        def test_mock_get(self):
            obj = CustomObject()
            self.expect(obj.get).args('name').returns('My Name')
            self.assert_equals(obj.get('name'), 'My Name')
            self.expect(obj.get).args('name').returns('Your Name')
            self.assert_equals(obj.get('name'), 'Your Name')

        def test_mock_get_with_at_most(self):
            obj = CustomObject()
            self.expect(obj.get).args('name').returns('My Name').at_most(2)
            self.assert_equals(obj.get('name'), 'My Name')
            self.assert_equals(obj.get('name'), 'My Name')
            self.assert_equals(obj.get('name'), 'My Name') # this one will fail

    if __name__ == '__main__':
        import unittest2
        unittest2.main()


.. _chai-api:

API
===

All of the features are available by extending the ``Chai`` class, itself a subclass of ``unittest.TestCase``. If ``unittest2`` is available Chai will use that, else it will fall back to ``unittest``. Chai also aliases all of the ``assert*`` methods to lower-case with undersores. For example, ``assertNotEquals`` can also be referenced as ``assert_not_equals``.

Additionally, ``Chai`` loads in all assertions, comparators and mocking methods into the module in which a ``Chai`` subclass is declared. This is done to cut down on the verbosity of typing ``self.`` everywhere that you want to run a test.  The references are loaded into the subclass' module during ``setUp``, so you're sure any method you call will be a reference to the class and module in which a particular test method is currently being executed. Methods and comparators you define locally in a test case will be globally available when you're running that particular case as well. ::
    
    class ProtocolInterface(object): 
        def _private_call(self, arg):
            pass
        def get_result(self, arg): 
            self._private_call(arg)
            return 'ok'
    
    class TestCase(Chai):
        def assert_complicated_state(self, obj):
            return True  # ..or.. raise AssertionError()

        def test_mock_get(self):
            obj = ProtocolInterface()
            data = object()
            expect(obj._private_call).args(data)
            assert_equals('ok', obj.get_result(data))
            assert_complicated_state(data)

Stubbing
--------

The simplest mock is to stub a method. This replaces the original method with a subclass of ``chai.Stub``, the main instrumentation class. All additional ``stub`` and ``expect`` calls will re-use this stub, and the stub is responsible for re-installing the original reference when ``Chai.tearDown`` is run.

Stubbing is used for situations when you want to assert that a method is never called. ::

    class CustomObject (object): 
        def get(self, arg):
            pass

    class TestCase(Chai):
        def test_mock_get(self):
            obj = CustomObject()
            stub(obj.get)
            assert_raises( UnexpectedCall, obj.get )

In this example, we can reference ``obj.get`` directly because ``get`` is a bound method and provides all of the context we need to refer back to ``obj`` and stub the method accordingly. There are cases where this is insufficient, such as module imports and special Python types such as ``object().__init__``. If the object can't be stubbed with a reference, ``UnsupportedStub`` will be raised and you can use the verbose reference instead. ::
    
    class TestCase(Chai):
        def test_mock_get(self):
            obj = CustomObject()
            stub(obj, 'get')
            assert_raises( UnexpectedCall, obj.get )

Stubbing an unbound method will apply that stub to all future instances of that class. ::
    
    class TestCase(Chai):
        def test_mock_get(self):
            stub(CustomObject.get)
            obj = CustomObject()
            assert_raises( UnexpectedCall, obj.get )

Finally, some methods cannot be stubbed because it is impossible to call ``setattr`` on the object. A good example of this is the ``datetime.datetime`` class.


Expectation
-----------

Expectations are individual test cases that can be applied to a stub. They are expected to be run in order (unless otherwise noted). They are greedy, in that so long as an expectation has not been met and the arguments match, the arguments will be processed by that expectation. This mostly applies to the "at_least" and "any_order" expectations, which (may) stay open throughout the test and will handle any matching call.

Expectations will automatically create a stub if it's not already applied, so no separate call to ``stub`` is necessary. The arguments and edge cases regarding what can and cannot have expectations applied are identical to stubs. The ``expect`` call will return a new ``chai.Expectation`` object which can then be used to modify the expectation. Without any modifiers, an expectation will expect a single call without arguments and return None. ::

    class TestCase(Chai):
        def test_mock_get(self):
            obj = CustomObject()
            expect(obj.get)
            assert_equals( None, obj.get() )
            assert_raises( UnexpectedCall, obj.get )

Modifiers can be applied to the expectation. Each modifier will return a reference to the expectation for easy chaining. In this example, we're going to match a parameter and change the behavior depending on the argument. This also shows the ability to incrementally add expectations throughout the test. ::

    class TestCase(Chai):
        def test_mock_get(self):
            obj = CustomObject()
            expect(obj.get).args('foo').returns('hello').times(2)
            assert_equals( 'hello', obj.get('foo') )
            assert_equals( 'hello', obj.get('foo') )
            expect(obj.get).args('bar').raises( ValueError )
            assert_raises( ValueError, obj.get, 'bar' )

Lastly, the arguments modifier supports several matching functions. For simplicity in covering the common cases, the arg expectation assumes an equals test for instances and an instanceof test for types. All rules that apply to positional arguments also apply to keyword arguments. ::

    class TestCase(Chai):
        def test_mock_get(self):
            obj = CustomObject()
            expect(obj.get).args(is_a(float)).returns(42)
            assert_raises( UnexpectedCall, obj.get, 3 )
            assert_equals( 42, obj.get(3.14) )
            
            expect(obj.get).args(str).returns('yes')
            assert_equals( 'yes', obj.get('no') )

            expect(obj.get).args(is_arg(list)).return('yes')
            assert_raises( UnexpectedCall, obj.get, [] )
            assert_equals( 'yes', obj.get(list) )

Modifiers
+++++++++

Expectations expose the following public methods for changing their behavior.


args(\*args, \*\*kwargs)
  Add a test to the expectation for matching arguments.

returns(object)
  Add a return value to the expectation when it is matched and executed.

raises(exception)
  When the expectation is run it will raise this exception. Accepts type or instance.

times(int)
  An integer that defines a hard limit on the minimum and maximum number of times the expectation should be executed.

at_least(int)
  Sets a minimum number of times the expectation should run and removes any maximum.

at_least_once
  Equivalent to ``at_least(1)``.

at_most(int)
  Sets a maximum number of times the expectation should run. Does not affect the minimum.

at_most_once
  Equivalent to ``at_most(1)``.

once
  Equivalent to ``times(1)``, also the default for any expectation.

any_order
  The expectation can be called at any time, independent of when it was defined. Can be combined with ``at_least_once`` to force it to respond to all matching calls throughout the test.

side_effect
  Called with a function argument. When the expectation passes a test, the function will be executed. The side effect will be executed even if the expectation is configured to raise an exception.
  

Argument Comparators
++++++++++++++++++++

Argument comparators are defined as classes in ``chai.comparators``, but loaded into the ``Chai`` class for convenience (and by extension, a subclass' module).

equals(object)
  The default comparator, uses standard Python equals operator

almost_equals(float, places)
  Identical to assertAlmostEquals, will match an argument to the comparator value to a most ``places`` digits beyond the decimal point.

instance_of(type)
  Match an argument of a given type. Supports same arguments as builtin function ``isinstance``.

is_a(type)
  Alias of ``instance_of``.

is_arg(object)
  Matches an argument using the Python ``is`` comparator.

any_of(comparator_list)
  Matches an argument if any of the comparators in the argument list are met. Uses automatic comparator generation for instances and types in the list.

all_of(comparator_list)
  Matches an argument if all of the comparators in the argument list are met. Uses automatic comparator generation for instances and types in the list.

not_of(comparator)
  Matches an argument if the supplied comparator does not match.
<commit_msg>Updated documentation to match 0.1.5 API<commit_after>=================================
 Chai - Python Mocking Made Easy
=================================

:Version: 0.1.5
:Download: http://pypi.python.org/pypi/chai
:Source: https://github.com/agoragames/chai
:Keywords: python, mocking, testing, unittest, unittest2

.. contents::
    :local:

.. _chai-overview:

Overview
========

Chai provides a very easy to use api for mocking/stubbing your python objects, patterned after the `Mocha <http://mocha.rubyforge.org/>`_ library for Ruby.

.. _chai-example:

Example
=======

The following is an example of a simple test case which is mocking out a get method
on the ``CustomObject``. The Chai api allows use of call chains to make the code 
short, clean, and very readable. It also does away with the standard setup-and-replay
work flow, giving you more flexibility in how you write your cases. ::


    from chai import Chai

    class CustomObject (object): 
        def get(self, arg):
            pass

    class TestCase(Chai):
        def test_mock_get(self):
            obj = CustomObject()
            self.expect(obj.get).args('name').returns('My Name')
            self.assert_equals(obj.get('name'), 'My Name')
            self.expect(obj.get).args('name').returns('Your Name')
            self.assert_equals(obj.get('name'), 'Your Name')

        def test_mock_get_with_at_most(self):
            obj = CustomObject()
            self.expect(obj.get).args('name').returns('My Name').at_most(2)
            self.assert_equals(obj.get('name'), 'My Name')
            self.assert_equals(obj.get('name'), 'My Name')
            self.assert_equals(obj.get('name'), 'My Name') # this one will fail

    if __name__ == '__main__':
        import unittest2
        unittest2.main()


.. _chai-api:

API
===

All of the features are available by extending the ``Chai`` class, itself a subclass of ``unittest.TestCase``. If ``unittest2`` is available Chai will use that, else it will fall back to ``unittest``. Chai also aliases all of the ``assert*`` methods to lower-case with undersores. For example, ``assertNotEquals`` can also be referenced as ``assert_not_equals``.

Additionally, ``Chai`` loads in all assertions, comparators and mocking methods into the module in which a ``Chai`` subclass is declared. This is done to cut down on the verbosity of typing ``self.`` everywhere that you want to run a test.  The references are loaded into the subclass' module during ``setUp``, so you're sure any method you call will be a reference to the class and module in which a particular test method is currently being executed. Methods and comparators you define locally in a test case will be globally available when you're running that particular case as well. ::
    
    class ProtocolInterface(object): 
        def _private_call(self, arg):
            pass
        def get_result(self, arg): 
            self._private_call(arg)
            return 'ok'
    
    class TestCase(Chai):
        def assert_complicated_state(self, obj):
            return True  # ..or.. raise AssertionError()

        def test_mock_get(self):
            obj = ProtocolInterface()
            data = object()
            expect(obj._private_call).args(data)
            assert_equals('ok', obj.get_result(data))
            assert_complicated_state(data)

Stubbing
--------

The simplest mock is to stub a method. This replaces the original method with a subclass of ``chai.Stub``, the main instrumentation class. All additional ``stub`` and ``expect`` calls will re-use this stub, and the stub is responsible for re-installing the original reference when ``Chai.tearDown`` is run.

Stubbing is used for situations when you want to assert that a method is never called. ::

    class CustomObject (object): 
        def get(self, arg):
            pass

    class TestCase(Chai):
        def test_mock_get(self):
            obj = CustomObject()
            stub(obj.get)
            assert_raises( UnexpectedCall, obj.get )

In this example, we can reference ``obj.get`` directly because ``get`` is a bound method and provides all of the context we need to refer back to ``obj`` and stub the method accordingly. There are cases where this is insufficient, such as module imports and special Python types such as ``object().__init__``. If the object can't be stubbed with a reference, ``UnsupportedStub`` will be raised and you can use the verbose reference instead. ::
    
    class TestCase(Chai):
        def test_mock_get(self):
            obj = CustomObject()
            stub(obj, 'get')
            assert_raises( UnexpectedCall, obj.get )

Stubbing an unbound method will apply that stub to all future instances of that class. ::
    
    class TestCase(Chai):
        def test_mock_get(self):
            stub(CustomObject.get)
            obj = CustomObject()
            assert_raises( UnexpectedCall, obj.get )

Finally, some methods cannot be stubbed because it is impossible to call ``setattr`` on the object. A good example of this is the ``datetime.datetime`` class.


Expectation
-----------

Expectations are individual test cases that can be applied to a stub. They are expected to be run in order (unless otherwise noted). They are greedy, in that so long as an expectation has not been met and the arguments match, the arguments will be processed by that expectation. This mostly applies to the "at_least" and "any_order" expectations, which (may) stay open throughout the test and will handle any matching call.

Expectations will automatically create a stub if it's not already applied, so no separate call to ``stub`` is necessary. The arguments and edge cases regarding what can and cannot have expectations applied are identical to stubs. The ``expect`` call will return a new ``chai.Expectation`` object which can then be used to modify the expectation. Without any modifiers, an expectation will expect a single call without arguments and return None. ::

    class TestCase(Chai):
        def test_mock_get(self):
            obj = CustomObject()
            expect(obj.get)
            assert_equals( None, obj.get() )
            assert_raises( UnexpectedCall, obj.get )

Modifiers can be applied to the expectation. Each modifier will return a reference to the expectation for easy chaining. In this example, we're going to match a parameter and change the behavior depending on the argument. This also shows the ability to incrementally add expectations throughout the test. ::

    class TestCase(Chai):
        def test_mock_get(self):
            obj = CustomObject()
            expect(obj.get).args('foo').returns('hello').times(2)
            assert_equals( 'hello', obj.get('foo') )
            assert_equals( 'hello', obj.get('foo') )
            expect(obj.get).args('bar').raises( ValueError )
            assert_raises( ValueError, obj.get, 'bar' )

Lastly, the arguments modifier supports several matching functions. For simplicity in covering the common cases, the arg expectation assumes an equals test for instances and an instanceof test for types. All rules that apply to positional arguments also apply to keyword arguments. ::

    class TestCase(Chai):
        def test_mock_get(self):
            obj = CustomObject()
            expect(obj.get).args(is_a(float)).returns(42)
            assert_raises( UnexpectedCall, obj.get, 3 )
            assert_equals( 42, obj.get(3.14) )
            
            expect(obj.get).args(str).returns('yes')
            assert_equals( 'yes', obj.get('no') )

            expect(obj.get).args(is_arg(list)).return('yes')
            assert_raises( UnexpectedCall, obj.get, [] )
            assert_equals( 'yes', obj.get(list) )

Modifiers
+++++++++

Expectations expose the following public methods for changing their behavior.


args(\*args, \*\*kwargs)
  Add a test to the expectation for matching arguments.

returns(object)
  Add a return value to the expectation when it is matched and executed.

raises(exception)
  When the expectation is run it will raise this exception. Accepts type or instance.

times(int)
  An integer that defines a hard limit on the minimum and maximum number of times the expectation should be executed.

at_least(int)
  Sets a minimum number of times the expectation should run and removes any maximum.

at_least_once
  Equivalent to ``at_least(1)``.

at_most(int)
  Sets a maximum number of times the expectation should run. Does not affect the minimum.

at_most_once
  Equivalent to ``at_most(1)``.

once
  Equivalent to ``times(1)``, also the default for any expectation.

any_order
  The expectation can be called at any time, independent of when it was defined. Can be combined with ``at_least_once`` to force it to respond to all matching calls throughout the test.

side_effect
  Called with a function argument. When the expectation passes a test, the function will be executed. The side effect will be executed even if the expectation is configured to raise an exception.
  

Argument Comparators
++++++++++++++++++++

Argument comparators are defined as classes in ``chai.comparators``, but loaded into the ``Chai`` class for convenience (and by extension, a subclass' module).

equals(object)
  The default comparator, uses standard Python equals operator

almost_equals(float, places)
  Identical to assertAlmostEquals, will match an argument to the comparator value to a most ``places`` digits beyond the decimal point.

is_a(type)
  Match an argument of a given type. Supports same arguments as builtin function ``isinstance``.

is_arg(object)
  Matches an argument using the Python ``is`` comparator.

any_of(comparator_list)
  Matches an argument if any of the comparators in the argument list are met. Uses automatic comparator generation for instances and types in the list.

all_of(comparator_list)
  Matches an argument if all of the comparators in the argument list are met. Uses automatic comparator generation for instances and types in the list.

not_of(comparator)
  Matches an argument if the supplied comparator does not match.

{'fraction_non_alphanumeric': 0.09229319579448522, 'fraction_numerical': 0.002132513390200357, 'mean_word_length': 3.3235420240137223, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3125715', 'n_tokens_mistral': 5362, 'n_tokens_neox': 5209, 'n_words': 2345}