Skip to content

Commit 65cdeca

Browse files
committed
Merge pull request #4 from arcivanov/issue_3
Add README and optimize
2 parents 4c95757 + cf0b2b9 commit 65cdeca

File tree

2 files changed

+39
-12
lines changed

2 files changed

+39
-12
lines changed

README.md

+27-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,28 @@
1-
# karellen-testing
1+
# Karellen Testing Frameworks and Utilities
22
Karellen Testing Frameworks and Utilities
3+
4+
## Mock
5+
A collection of Mock utilities helping with common tasks
6+
7+
### Spy
8+
9+
```python
10+
from unittest import TestCase
11+
from karellen.testing.mock import MagicSpy
12+
13+
14+
class Class_A(object):
15+
def method_X(self):
16+
self.method_Y()
17+
18+
def method_Y(self):
19+
pass
20+
21+
22+
class TestSpy(TestCase):
23+
def test_class_a_api(self):
24+
mock = MagicSpy(Class_A())
25+
26+
mock.method_X()
27+
mock.method_Y.assert_called_once_with()
28+
```

src/main/python/karellen/testing/mock/__init__.py

+12-11
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@ class Spy(object):
4343

4444
def __init__(self, mock):
4545
self.__dict__["_%s__mock" % self.__class__.__name__] = mock
46-
self.__dict__["_%s__wrapped" % self.__class__.__name__] = self.__mock._mock_wraps
4746

4847
def __getattr__(self, item):
4948
attr = getattr(self.__mock, item)
5049

5150
mock_wraps = attr._mock_wraps
51+
wrapped = self.__mock._mock_wraps
5252
if mock_wraps is not None:
5353
# Is the wrapped value present?
5454
if isinstance(mock_wraps, types.MethodType):
@@ -58,16 +58,15 @@ def __getattr__(self, item):
5858
# If the method belongs to a Mock and method is not class method
5959
# Rebind mock method to use this Spy as self
6060
# This will allow self.method() to go back into the spy and into the Mock to be tracked
61-
attr._mock_wraps = types.MethodType(attr._mock_wraps.__func__, self)
61+
attr._mock_wraps = types.MethodType(mock_wraps.__func__, self)
6262
else:
6363
# This attribute is not a method
64-
if not isinstance(mock_wraps, types.FunctionType) and hasattr(self.__wrapped, item):
64+
if not isinstance(mock_wraps, types.FunctionType) and hasattr(wrapped, item):
6565
# If wrapped is not a function (e.g. static method) and the underlying wrapped
6666
# has this attribute then simply return the value of that attribute directly
67-
return getattr(self.__wrapped, item)
67+
return getattr(wrapped, item)
6868
else:
69-
if attr._mock_return_value is DEFAULT and hasattr(self.__wrapped, item) and getattr(self.__wrapped,
70-
item) is None:
69+
if attr._mock_return_value is DEFAULT and hasattr(wrapped, item) and getattr(wrapped, item) is None:
7170
# This attribute is not wrapped, and if it doesn't have a return value
7271
# and is None then just return None
7372
return None
@@ -76,20 +75,22 @@ def __getattr__(self, item):
7675
return attr
7776

7877
def __setattr__(self, key: str, value):
78+
mock = self.__mock
7979
try:
80-
attr = getattr(self.__mock, key)
80+
attr = getattr(mock, key)
8181
mock_wraps = attr._mock_wraps
82+
wrapped = mock._mock_wraps
8283
if mock_wraps is None or \
83-
isinstance(mock_wraps, types.MethodType) and mock_wraps.__self__ in (self, self.__wrapped):
84+
isinstance(mock_wraps, types.MethodType) and mock_wraps.__self__ in (self, wrapped):
8485
# If attribute is not wrapped or is a method of the wrapped object and method is bound
8586
# to Spy or spied mock then delegate to Mock
86-
return setattr(self.__mock, key, value)
87+
return setattr(mock, key, value)
8788

8889
# Otherwise set the value directly on the object that is wrapped and is spied on
89-
setattr(self.__wrapped, key, value)
90+
setattr(wrapped, key, value)
9091
except AttributeError:
9192
# If Mock doesn't have this attribute delegate to Mock
92-
return setattr(self.__mock, key, value)
93+
return setattr(mock, key, value)
9394

9495

9596
def magic_spy(obj):

0 commit comments

Comments
 (0)