Skip to content

Instantly share code, notes, and snippets.

@ramiroluz
Created October 9, 2013 17:50
Show Gist options
  • Save ramiroluz/6905320 to your computer and use it in GitHub Desktop.
Save ramiroluz/6905320 to your computer and use it in GitHub Desktop.
Patching and mocking Plone unittests with using mock
----------------------------------------------------
The python mock module was created by Michael Foord and it has been integrated in the unittest module since python version 3.3. It's license is BSD as it is possible to see in the official `homepage of the project`_.
.. _homepage of the project: http://www.voidspace.org.uk/python/mock/index.html
It allows you to replace parts of your system under test with mock objects and make assertions about how they have been used.
You can use it to replace methods or properties, as it will be shown in the following examples.
The examples where extracted from the Module :py:mod:`plone.recipe.codeanalysis`.
Mocking a method
~~~~~~~~~~~~~~~~
The first thing to do is import the necessary classes.
.. code-block:: python
:emphasize-lines: 3,4
import unittest
from plone.recipe.codeanalysis.jshint import code_analysis_jshint
from mock import MagicMock
from mock import patch
Then the test method have to be decorated with @patch, the method needs an extra argument that represents the class being decorated.
.. code-block:: python
:emphasize-lines: 2,3
class TestJSHint(unittest.TestCase):
@patch('subprocess.Popen')
def test_analysis_should_return_false(self, mock_class):
Inside the method a MagicMock is created. The desired value is set by passing a return_value parameter to the MagicMock class. In this case, communicate needs to return a tuple with two values, the second one is not necessary in the code being tested, you can choose whatever you want.
.. code-block:: python
:emphasize-lines: 3,5
@patch('subprocess.Popen')
def test_analysis_should_return_false(self, mock_class):
mock_class().communicate = MagicMock(
return_value=(' x (E000) x ', 'IGNORED ERR',)
)
Finally you import the class being Mocked. The rest of the code is needed to give call the function being tested.
.. code-block:: python
:emphasize-lines: 1
from subprocess import Popen
options = {'jshint-bin': 'FAKE_EXECUTABLE',
'jshint-exclude': 'FAKE_EXCLUDE',
'directory': 'FAKE_DIRECTORY',
'jenkins': 'False'}
self.assertFalse(code_analysis_jshint(options))
Mocking a property
~~~~~~~~~~~~~~~~~~
To mock a property you need to import an additional class.
.. code-block:: python
:emphasize-lines: 5
import unittest
from plone.recipe.codeanalysis.flake8 import code_analysis_flake8
from mock import MagicMock
from mock import patch
from mock import PropertyMock
In this use case, we don't care about the value of the method communicate, but the code being tested, code_analysis_flake8, expect the method communicate to return a tuple, so it is mocked up. A PropertyMock have to be created, then the property mock have to be assigned to the equivalent property name of the type being mocked. The rest of the code is similar.
.. code-block:: python
:emphasize-lines: 4,8
class TestFlake8(unittest.TestCase):
@patch('subprocess.Popen')
def test_analysis_should_return_false(self, mock_class):
mock_class().communicate = MagicMock(
return_value=(' x Error should return false x ', 'IGNORED ERR',)
)
returncode = PropertyMock(return_value=1)
type(mock_class()).returncode = returncode
from subprocess import Popen
The mock technique is very useful when you don't want to depend on external libraries, in our example, the subprocess.Popen.
.. seealso::
Module :py:mod:`mock`
Documentation of the :py:mod:`mock` module.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment