For instance, if I have a call to the split method (i.e. some_string.split(":") ) Is is possible to mock this. I wanted to assert that the split function is called using assert_called_once_with
-
3That sounds like too fine-grained a test, focusing on the wrong details. Why not focus on the *output* of your unit instead? – Martijn Pieters Nov 12 '14 at 11:48
-
I think you cannot do that either for method and for an string object because the attribute `split` of the string is read only – Michele d'Amico Nov 12 '14 at 13:07
2 Answers
I confirm you can't do that because split() is a built-in attribute of str object and you can't set attributes of built-in or extension because they are readonly.
Below some inconclusive tests after trying into a Python 2.7.10 interpreter
>>> __builtins__.str.split
<method 'split' of 'str' objects>
>>> type(__builtins__.str.split)
<type 'method_descriptor'>
Trying to override it using a function
>>> type(lambda f:f)
<type 'function'>
>>> __builtins__.str.split = lambda f: f
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'str'
Trying to override it using a callable (function or method)
>>> type(callable)
<type 'builtin_function_or_method'>
>>> __builtins__.str.split = callable
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'str'
After having a look more deeply into the CPython source code here [1]. It's a limitation in Objects/typeobject.c introduce by the function list below. This function check if we try to set a readonly attribute and raise TypeError.
type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
{
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
PyErr_Format(
PyExc_TypeError,
"can't set attributes of built-in/extension type '%s'",
type->tp_name);
return -1;
}
if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
return -1;
return update_slot(type, name);
}
[1] https://hg.python.org/cpython/file/tip/Objects/typeobject.c#l3022

- 893
- 11
- 9
Yes it is with a couple of caviats. In my case I have successfully mocked str in python3 so I can assert that split is being called with a specific input
There are two caviats
- With patch, I replaced the original str class with a new class that inherits from str
- In the code that I was testing, I had to do a redundant string casting like
str(str_val).split
Here's how one can do it:
class MyStr(str):
def split(self, sep=None, maxsplit=-1)):
expected_str = "some_input_mutated_inside_fn_before_split_called"
self.assertEqual(self, expected_str)
return super().split(sep=sep, maxsplit=maxsplit)
with patch('mymodule.str', new=MyStr):
output = mymodule.function_that_calls_string_split(
"some_input"
)

- 2,136
- 1
- 21
- 28