I would recommend to use the most simple and shortest approach, which is in Python probably option A, monkey patchingmonkey patching. Everything else makes the code more complicated, with no substantial benefit. The only thing I would change here is to make this solution more explicit (see below).
Converting your module to a class (option C) should be motivated by the fact you need a class instead of a module, not by the fact you want to mock a single function. This option is preferable in languages like Java or C#, where there is no distinction between a "module" and a "class" in a way which is comparable to Python, and where a function like func would probably "live" inside a class at first hand.
Replacing the method do_something by an object with a method (option D) should not be done for the same reason - use an object instead of a standalone function when a standalone function is "not enough", for the sheer purpose of mocking it brings no advantage.
So this leaves you with option B or A. However, I would not pollute the parameter list of func for the purpose of mocking, nor would I stuff mocking code into a function func just for the purpose of mocking if there is simpler alternative. Better keep the "official" API as as well the functional code as clean as possible. Imagine what happens if you want to mock more "private" methods instead of just one, and if you want to test 5 public methods of the module - the API will contain 90% parameters only used for testing, which decreases the readability by a magnitude.
If you have concerns about the monkey patching approach because it changes something private, you can add a "public" (or "internal") patching function to the module, which allows the manipulation to be done through an "official" function:
def setup_mock_for_do_something(do_something): _do_something = do_something This makes the possibility of "monkey patching" in this module more explicit, by simultanously avoiding unneccessary structural changes to the whole code.
So why is this all more "Pythonic"? Looking at "The Zen of Python", we see the following principles applied here
- Explicit is better than implicit.
- Simple is better than complex.
- Readability counts.
- If the implementation is easy to explain, it may be a good idea.