Last active
March 9, 2016 16:12
-
-
Save burkestar/39e2f0f0e6090dd8760d to your computer and use it in GitHub Desktop.
pytest test class that works with pytest fixtures and has some magic for scaffolding patchers that are reused across all class test methods
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from collections import OrderedDict | |
| import pytest | |
| class MagicalPatchersMixin(object): | |
| """ | |
| A mixin for pytest xunit-style classes that provides scaffolding for setting up | |
| patchers more easily that are setup for each class test method. | |
| Additionally, a corresponding mock instance is created for each patch and attached to | |
| the instance of the class. The naming of the mock is done by convention like the following: | |
| self.create_patcher('some.sample_module.sample_method') | |
| will create self.mock_sample_method with snake_case. | |
| self.create_patcher('some.sample_module.SampleClass') | |
| will create self.MockSampleClass with CamelCase | |
| The mixin ensures the patchers are stopped during teardown of each method. | |
| """ | |
| def create_patcher(self, target): | |
| self.patchers[target] = patch(target) | |
| mock = self.patchers[target].start() | |
| import importlib | |
| import types | |
| module_parts = target.split('.') | |
| if len(module_parts) > 1: | |
| target_module = importlib.import_module(".".join(module_parts[:-1])) | |
| target_obj = module_parts[-1] | |
| if type(target_module.__dict__[target_obj]) in [types.ClassType, types.TypeType, types.ObjectType]: | |
| # CamelCase - assume target_obj is already CamelCase | |
| setattr(self, 'Mock{}'.format(target_obj), mock) | |
| else: | |
| # snake_case - assume target_obj is already snake_case | |
| setattr(self, 'mock_{}'.format(target_obj), mock) | |
| return mock | |
| def setup_method(self, method): | |
| self.patchers = OrderedDict() | |
| def teardown_method(self, method): | |
| for patcher in self.patchers.itervalues(): | |
| if patcher: | |
| patcher.stop() | |
| @pytest.fixture | |
| def some_fixture(): | |
| return { | |
| 'some': 'data' | |
| } | |
| class TestRun(MagicalPatchersMixin): | |
| def setup_method(self, method): | |
| super(TestRun, self).setup_method(method) | |
| for p in ['some.sample_module.sample_method', | |
| 'some.sample_module.SampleClass']: | |
| self.create_patcher(p) | |
| def setup_class(self): | |
| # fixtures | |
| self.some_fixture = some_fixture() | |
| def teardown_method(self, method): | |
| super(TestRun, self).teardown_method(method) | |
| def test_some_things(self): | |
| assert type(self.some_fixture) == dict | |
| self.mock_sample_method.return_value = 3 | |
| from some.sample_module import sample_method | |
| assert sample_method("1234") == 3 | |
| self.mock_sample_method.assert_called_with("1234") | |
| from some.sample_module import SampleClass | |
| c = SampleClass(value=True) | |
| self.MockClass.assert_called_with(value=True) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment