I have this class static method in python 3, with necessary test code
example.py:
class ExampleClass{
...
@staticmethod
def get_new_id: str, id: str):
return {
"new_id": "{}_{}_{}".format(
datetime.utcnow().strftime("%Y%m%d%H%M%S"),
id,
name
),
"old_id": id
}
...
}
test_example.py:
...
class TestExampleId(TestCase):
@patch("example_folder.example.datetime", Mock(utcnow=Mock(return_value=datetime(1992, 1, 26, 12, 0, 0))))
def given_id_name_example_object_returned(self):
self.assertEqual(
{
'old_id': 'xxx',
'new_id': '19920916120000_xxx_test_name'
}, ExampleClass.get_new_id("xxx", "test_name")
)
This is about it in terms of what mutations can do. But in mutmut, there is one surviving mutation, and it is the removal of @staticmethod
. So i have two questions:
Thanks.
Came across the same thing, did some digging, here's what I found...
The tests pass because, in Python 3, calling a method on the class itself (rather than an instance of the class) runs the method as if it were static. I've not been able to find an 'official python' bit of documentation to cite for that, but can cite the accepted answer to this StackOverflow question. If anyone finds this actually documented anywhere, then edits are welcome!
Given that that's just how Python 3 works, it got me thinking about why that @staticmethod
decorator is even needed. Since Python 3 will call the method statically when called on a class anyway, the decorator is only serving a purpose when we call it on an instance of the class. So you could probably do something like:
class TestExampleId(TestCase):
@patch("example_folder.example.datetime", Mock(utcnow=Mock(return_value=datetime(1992, 1, 26, 12, 0, 0))))
def given_id_name_example_object_returned(self):
instance = ExampleClass()
self.assertEqual(
{
'old_id': 'xxx',
'new_id': '19920916120000_xxx_test_name'
}, instance.get_new_id("xxx", "test_name")
)
... and then the mutation will fail because the method inputs aren't right. In my case, my static methods are actually factory methods for the class, and it's nonsense to call my factory methods on an instance of the class. I decided to throw a particular error if the user tried to call a factory method on an instance of the class, and tested that that error was being thrown, that then covered the @staticmethod
decorator. Once again, thoroughly testing has got me really thinking about the structure of my code!