OOP mitigates this particular problem with encapsulation.
When calling a method (from the outside) you don't know what what internal attributes may be read and modified. But in OO you shouldn't know or care.
More generally, the "unit" you reason about and test is the object, not the function. So internal attributes are like local variables inside a function: You don't care about them when calling the function, you only care about observable behavior and input/output.
When testing objects, you don't test by inspecting the internal state. That would indeed be cumbersome and fragile. Instead you test the behavior of the object through its public interface.
I really dislike
bar examples, so lets take a more realistic example. Lets say you have an ordered dictionary:
let dict = OrderedDictionary()
print dict["horse"] --> cheval
print dict --> voiture
This dictionary could be implemented in multiple ways, e.g. a linked-list of key-value pairs, a hashtable combined with an array and so on. It could even change strategy based on the number of items. The point is that you don't care as long as it works.
Now consider if all parameters had to be explicit:
dict_h = HashTable()
dict_l = Array()
dict_Add(dict_h, dict_l, "car", "voiture")
dict_Add(dict_h, dict_l, "horse", "cheval")
print dict_by_key(dict_h, "horse") --> cheval
print dict_by_index(dict_l, 0) --> voiture
Here it is explicit that e.g.
dict_by_key only uses the hashtable and not the array. But the price for this explicitness is really steep: You push complexity and implementation details to the clients which spreads it all over the program and makes it much more difficult and risky to change the implementation. (Never mind that a Hashtable in itself would consist of multiple attributes)
Functional languages typically solve this by using record types which may contain multiple fields. But then you are back to the square one, that you don't know exactly which of these fields aRE read or modified by a function call.