First I'll introduce some axioms...
1. Mixing unit and integration testing is not ideal. If you are doing unit testing focus on verifying the code paths within the unit. When considering the collaborators focus on the finite number of states that are most likely to occur.
2. (I know this is contentions) Design your code so that it is easy to unit test. It'll be ok I promise. In almost all cases code designed for easy unit testing is synonymous with well engineered code
Imagine this python...
```
def get_a_file_and_do some_stuff(path):
d = dict()
with open(path, 'r') as file:
for line in file:
d[line] = d.get(line, 0) + 1
# a bunch of code that does something with this dictionary
return result
```The above is miserable to test with mocks. You end up trying to man handle the file object. Its not fun.
```
def get_a_file_and_do some_stuff(path):
d = convert_path_to_dictionary(path)
# a bunch of code that does something with this dictionary
return result
```With the simple flick of the wrist I can now trivially mock out "convert_path_to_dictionary(path)" and I only ever have to work with dictionaries.
So you might say "but but what about the file dictionary code. are you going to test that?" Probably not and if experience is any indicator I'll never have an issue with it. The edge cases and regressions will all lie in the custom business logic executed on the dictionary.
I see engineers make their lives enormously difficult to live up to some unachievable standard. Often that standard yields very little value in excess of a much simpler approximation