7 ms·
I think you've inadvertently backed up the GPs point. DI makes it impossible to have single units of work, as you are quite literally injecting other units into
by scaryclam 7y ago
I think you've inadvertently backed up the GPs point. DI makes it impossible to have single units of work, as you are quite literally injecting other units into that unit. Mocking is all find and good, but in the case of DI, you're not mocking because you have an external interface, you're mocking because you don't have single units.
- 0x445442 7y agoOne of the best ways I've found to illuminate poor designs is to not allow the use of mocking frameworks and then see how much pain follows in the test code.
- mistahenry 7y agoLet’s say I have a newsletter generation service for a website. Users can opt of emails. I use AWE SES for sending emails. I would like to test that emails are sent unless you’ve opted out. First, I don’t know how to accomplish this without mocking / replacing the email dependency before testing, even in an integration style test. Emails cost and how would I even verify if I did send the email. Second, I really am failing to see how this is poor design and am genuinely curious. I can think of so many scenarios where some “orchestrating” service uses multiple single unit services to pull off some task. To me, mocking is incredibly useful for ensuring that you test a lot of failure cases in these scenarios by mocking to force an exception. A lot can be accomplished via integration tests rather than mocking (actually using a database for example). But to somehow suggest mocking is across the board bad seems like an illogical position
- 0x445442 7y agoInstead of relying on a mocking framework to mock the injected service, can you inject a service which implements an interface and then supply whatever type of implementation you like depending on the context you'd like to test? This is one of the neat things about Java 8 Functions. I'm not sure about what their design decisions were but effectively they backed into coding to interface vs. implementation, which used to be a solid design principle.
- LgWoodenBadger 7y agoYou could, but it’s tedious to write a new class for every scenario you want to test. Or to program for every scenario within that test implementation. And now you kind of should test your test implementation, and who wants to do that? Mocking makes it quick, easy, straightforward, and cuts out a ton of code noise from the test.
- sasmithjr 7y agoThe email service I use has a library that allows me to set the Host as a constructor parameter, so I have an EmailHost environment configuration value that I change. It's localhost for dev/testing and set to the correct host for production. My test code then listens for the API calls from that library, uses a different library to parse the email, and then I can verify the content. No code changes between dev, test, and production, but I added an environment variable to represent the e-mail host so that I can change where e-mails are sent as needed. I dislike mocking, so I took the time to figure that out and set it up. If mocking works for you, I likely wouldn't go out of my way to change things to get rid of it.
- commandlinefan 7y agoWait a minute, your test code spins up a SERVER? Dear God, that’s just horrible… Sorry, man, but that’s just… horrible.
- kixiQu 7y agoThat's actually really common for distributed systems stuff as a way to run something in between full integration and unit tests. I think there's some official support for, e.g. local file stuff mimicking the interface of DynamoDB.
- hderms 7y agoI think separating code into 'algorithms' that make a decision based on pure data, returning the decision in the form of pure data, 'side effects' which represent actions which can occur (and are also very mockable), and then 'coordinators' which rig these things together. In a dynamically typed language this pattern can be more challenging because there is a lot more burden to prove that the integration of various components is rigged up properly, but in a language like Scala this can work incredibly well. The bulk of the testing in this design would be easy to write unit tests around the 'algorithm' functions. The coordinators have almost zero logic in them and so are mostly just there to coordinate components together and generally demand less unit testing. Since the coordinators coordinate actions and algorithms then they'd likely have dependencies (the actions in this case, generally) passed in using constructor injection or parameters to a function.
- nkohari 7y ago> you are quite literally injecting other units into that unit In tests, you will usually inject a mock of any dependencies that you don't want to test, in order to isolate the unit you do want to test.
- ryeguy 7y agoI'm confused by what you mean. DI doesn't introduce any more dependencies compared to a non-DI approach (like hardcoding dependencies via module import or static methods), so how does it change the unit of work? DI doesn't add additional things to mock. DI simply means that dependencies are explicitly passed in. In a non-DI approach, they are implicit but still exist.
- Gibbon1 7y agoDI with sane defaults can reduce the min number of dependencies. The other thing DI does is make it easier to crate acyclic layered dependencies. Which promotes true modularity and reduces churn.