4 ms·
Dependency injection of just the function you need is good, but is still the second best approach. The best approach is separating the calling of the external
by BoiledCabbage 3y ago
Dependency injection of just the function you need is good, but is still the second best approach.
The best approach is separating the calling of the external dependency (getting the time) to it's own function, and pass in the results of that call to your main function. Then test your main function. Ie approach 5. This eliminates the need for mocks and creates better factored code.
- theamk 3y agoThat works until you have a function that implements timeout and so needs to query time multiple times per invocation.
- JimmyAustin 3y agoThe function you pass in can keep track of how many times it’s been called, and provide different values on each call.
- Anon1096 3y agoThis is way way worse than passing in a timer and destroys your ability to write multi threaded code (among other defects).
- greiskul 3y agoThis is for unit testing. Even if your unit tests run in parallel, each test case will have it's own mocked clock. This is not even an experimental approach, the mocking of timers is literally one of the canonic examples of the advantages of dependency injection. I have written this exact code multiple times, on my own, or refactoring others people untestable code to this pattern to be able to test them. A couple of times the reason for the refactoring was specifically to demonstrate the existence of race conditions, to have a test case that has the race condition be deterministic so we could fix it and be sure of the fix working.
- BoiledCabbage 3y agoAgreed, so the preference order is 1. Pass in the data you need 2. Pass in a function that gives you the data you need 3a./3b. Pass in an object (or function like a factory) that gives you the function that gets you the data you need.
- micw 3y ago> Pass in a function that gives you the data you need That's basically dependency injection but on a per-method basis. I'd see this as an anti pattern because it makes function calling _way_ more complex and error-prone.
- berkes 3y ago> it makes function calling First: no, not necessarily. Second: when you limit yourself to function calling, then yes, the problem is more pronounced (but not always there, see 1) In OOP, you can have factories, or constructors, or even entire design patterns to help solve this. In FP, there are closures, that can solve this exact problem for you: the dependency -e.g. a timekeeper- is captured in the closure. Dependency injection is, by no means, limited to `do_the_thing(variable, depency1, depency2, dependency3)`. I see this argument too often used to counter the idea of DI, and it is silly: it shows above all that the person countering the idea has little experience with all the surrounding concepts to support DI. And that brings me back to 1: There's so much more that can be DI-d: objects can be instantiated with dependencies passed in, factories can do this. There are Actors, Workers, Decorators, Factories. Hell even a superglobal `config.get_timekeeper()` might work in some situations.
- LouisSayers 3y agoYou can extract the timeout functionality into its own structure then and mock it out during testing. There's usually a solution of some sort, it might just require a bit of rearranging and extraction of concerns.
- aaomidi 3y agoThat’s basically dependency injection
- LouisSayers 3y agoYes, that's my point - they're giving an example for why "you can't do it", and I'm saying even for the example they just gave you can. Create a Timeout class and subscribe to it for a given interval with a callback to what needs to be run. Pass it in as a dependency which can be mocked during testing. Not sure why the downvotes...
- deleted 3y ago[deleted]
- munksbeer 3y ago>The best approach is separating the calling of the external dependency (getting the time) to it's own function That is the FP maximalist approach and many of us reject it. So no, it is not the best approach. We use a time provider interface and mock it, and have zero problems with this approach.
- marcosdumay 3y agoThe FP maximalist is writing your code in a monad that implements a timer. It looks quite similar to the OOP maximalist people are pushing here.
- hnfong 3y agoSo, "yes, we're writing the same thing, but because monads I'm still pure and you're not"?
- marcosdumay 3y agoKinda yes. And technically, it's still pure. It does fit well a small set of problems and break in surreal nightmarish ways for everything else; just like the OOP's maximalist DI. It is worse in that you are injecting an entire interpreter instead of just a few functions, but this doesn't create as many problems on practice as it looks like it should. And it's better because idiomatically the injection tends to be explicit and well defined; and if your DI doesn't have at least one of those, it will again break in surreal nightmarish ways. (And here I have to point out that in OOP-land, web frameworks are allergic to well defined interfaces - what means that you can create one, but if you insist the framework tends to choke and die. So, if you are writing for the web in OOP, there's actually only one option.) But well, it's not surprising that the maximalist options all break in similar ways.
- strulovich 3y agoI used to think this way, but now I believe that any method requiring writing code in a different way than the more natural one for the sake of tests is suboptimal. People generally call time functions, and a good testing setup should keep your code the same (or mostly the same). Anything else makes testing less approachable, and requires extra time from developers. The provider and mocking (or a fake if you got one) approach is probably the best you can get with most programming languages.