Reputation: 517
I have a big ugly function that I would like to refactor. Schematically, the function takes 2 objects, accesses several attributes and methods on these objects, combine them and use them and combination results to instantiate a result object.
It looks like this:
def ugly(obj_a, obj_b):
a_1 = obj_a.a_1
sub_a_1 = obj_a.get_sub()
r_1 = obj_b.combine(a_1)
if r_1 < 0:
raise ValueError
if not sub_a_1.validate():
raise Exception
return ResultObj(a_1, r_1, sub_a_1.get_matrix(), obj_b.some_other_attribute)
except that there are many attributes and methods accessed on both objects, and many constructor values passed to ResultObj.
This function is a nightmare to unit/integration test. As it stands I would have to mock obj_a and obj_b and all their methods and attributes.
What do you suggest for refactoring this type of code? Ideally I wouldn't have to change implemations of obj_a and obj_b.
This is in Python, so both OO and functional-ish designs work.
Thank you
Upvotes: 1
Views: 734
Reputation: 13779
One way of making this kind of procedural code more testable is to add "seams", i.e. split the code into units that can be run separately. It's hard to answer your question in the abstract because the logical seams are hard to see without actual code.
You could return the arguments list for a ResultObj rather than creating one directly. This allows you to test the process rather than also testing ResultObj. And it's easy to do ResultObj(*args)
afterward. However, you're still dealing with a lot of values.
Possibly you could have separate functions that access and validate data from A and B respectively, then return simple "flat" data structures (e.g. namedtuple) that are combined by a third function.
Beyond that, refactoring the classes themselves makes the most sense to me, though you said you'd rather avoid it. It would help if creating A and B objects is free of side effects or too many other dependencies, but again that depends on your specific code.
Also consider that "combine two objects into a mega-object" is inherently more of an integration-test case than a unit-test case.
Upvotes: 1