5 ms·
You can mock injected entities in tests, instead of e.g. always connecting to the db
by lhorie 5y ago
You can mock injected entities in tests, instead of e.g. always connecting to the db
- knuthsat 5y agoWith JS, you can easily mock a whole module implementation so to use DI so that you can mock the injected entities during testing feels unnecessary.
- eyelidlessness 5y agoI assume you’re referring to mocking like that provided by Jest? If so… I’ll add a two big caveats: 1. The mechanism used (clearing or overriding the cache) isn’t available for ESM, where mocking is significantly more challenging. 2. That mechanism is also responsible for a lot of memory leaks when any modules are stateful.
- postalrat 5y agoDoes bending your design to make it easier to mock result in better designs?
- eyelidlessness 5y ago1. It can promote simpler and easier to understand interfaces, but doesn’t guarantee it. 2. That doesn’t mean they’re “right” or necessarily “better”. 3. It depends a lot on the complexity of the code under test. If it’s really simple code—and especially if the stuff you intend to mock performs well and doesn’t mutate—making it easier to mock is probably going the wrong direction. 4. For most (>50%) things, yes. 5. But also for most things (off the cuff guess: >75%), you might be better off not mocking anyway. 6. If you (like me) prefer unit tests, you’ll probably get even better results by isolating as much of your code as possible from anything you’d want to mock in the first place.
- jitl 5y ago“Dependency injection”, especially this kind implementation, is fancy words for “calling your functions with commonly used parameters”. The language itself provides function parameters as part of the syntax of functions. Isn’t it easier and more clear to pass a dependency as a function parameter rather than mocking global state?
- bitwize 5y agoHave you ever worked in an enterprise environment? This kind of transformation happens all the time: <straightforward implementation> called <simple name> => <complicated implementation of same thing> called <fancy name> because <scalability, compliance, "best practice", or other BS reason>.
- lhorie 5y agoThe point is not so much how easy it is to mock, it's getting visibility into what should be mocked. Mocking moment.js with jest is relatively trivial, but your test is not going to warn you that your mock became inert if the implementation switched to date-fns. I've seen a number of occasions where a test using a mock passes one day then fails the next day because it turned out the mock was setup incorrectly, and the test was actually relying on wall clock, and it wasn't at all obvious that there was an issue because you could not statically analyze whether the mock was applied correctly.
- Sytten 5y agoThat is not really a good argument as you can also easily mock imported singletons in node. It is a bit dirtier but totally possible. Dependency injection was developed for languages that could not easily do that (like Java). It is just a design pattern that isnt that useful in python or js. You can decide to follow it if you think it will make your code cleaner/more readable.
- lhorie 5y ago> languages that could not easily do that (like Java) It's interesting that you bring up Java, which compiles things into class files. You can technically "mix and match" things there to achieve mocking semantics by carefully constructing jars. The point of DI is to bring composability into the realm of the language. In JS, sure you can use complex machinery to get jest to mock out imports during a hidden compilation phase, but there's no contractual relationship between the mock and the API consumption deep in your unit. E.g. I could "mock" moment.js, but if the implementation switches to date-fns, then the mock is still a valid thing, but it's no longer useful/applicable. Whereas, if there is a contract that can be enforced via types/API/DI, then switching date util implementations will trigger compile errors.
- jitl 5y agoWell said. Another reason passing dependencies as a parameter is great is to enforce constraints for a functional call. Say you have a database service that pools connections to your database. If you want to call some general function loadPermissionRecords(services, id) and be sure it only uses one connection for this invocation, you can wrap the database service with a facade that implements your resource constraint when calling it, eg loadPermissionRecords({ …services, database: database.withConnectionLimit(1) }, id) Sure, you could add the connection limit as a parameter to loadPermissionRecords, but you’ll also have to pass it down to every other function in the call graph beneath loadPermissionRecords…
- williamdclt 5y agoYou wouldn't inject Moment though, you'd inject an instance of a class (`DateManager` or whatever). You can absolutely write a mock for it that implement the same interface to enforce the same contract (`class DateManagerMock implements DateManager`), and use Jest mocking. Is it simpler/cleaner than DI? Arguable