6 ms·
Unit testing, leading to dependency injection, leading to IoC containers, leading to lots of tools to assemble, intercept and rewrite code at execution time. B
by barrkel 6y ago
Unit testing, leading to dependency injection, leading to IoC containers, leading to lots of tools to assemble, intercept and rewrite code at execution time.
But mostly unit testing. The commitment to isolated testable units of code is what generates abstractions of dubious value.
Don't use a class where you could use an interface: easier to mock. Don't use a static method where you could use an instance method on a collaborator: easier to inject, replace and mock. Your object graph getting too hard to compose, or other design smells? Don't worry, we have tools to handle the complexity, so you don't have to try and reduce it.
- throw_m239339 6y ago> Unit testing, leading to dependency injection, leading to IoC containers, leading to lots of tools to assemble, intercept and rewrite code at execution time. it's funny since in theory TDD should reduce API surface as it is supposed to make people think about API before implementation. But then , TDD and DDD intersect and it's not much TDD anymore when the domain model has to be implemented...
- alquemist 6y agoDon't mock. Test production code, maybe with faked storage.
- mschuster91 6y agoOften enough software has dependencies on external services (think of stuff like a CRM database, payment and shipping providers, integrations with CDNs, external identity verification services) where one has to go with mocks for testing if stuff like error handling etc. works.
- alquemist 6y agoFake external storage / rpc dependencies.
- vbezhenar 6y agoThat's called mocking...
- alquemist 6y agoMock: Adhoc guessing of what methods called on a dependency, but sometimes even between classes in the same module, might return. Guess repeated over and over as new tests are added, sometimes tens of times or even more. For example, https://site.mockito.org/#how https://site.mockito.org/#how, "when(mockedList.get(0)).thenReturn("first"); System.out.println(mockedList.get(0)); // prints "first"" Fake: A replacement module that behaves like a production module, but with certain simplifications, for example in-process vs. using rpcs, or simply cleaning up the filesystem after usage. For example, https://github.com/tk0miya/testing.postgresql https://github.com/tk0miya/testing.postgresql. "automatically setups a postgresql instance in a temporary directory, and destroys it after testing".
- kstenerud 6y agoSo... mock the storage? This very quickly becomes a slippery slope.
- alquemist 6y agoFake, don't mock. Write, or ask the team that provide the external dependency to write, a small piece of code that behaves like your external dependency, but in-process. You'll thank me after about the 47th time you're guessing (inconsistently, possibly incorrectly, and definitely overly verbose) how the external dependency actually works. Ban mocking libraries.
- kstenerud 6y agoIsn't that effectively the same thing? The fake implementation needs to mimic the API your software expects to run against, and must give valid-seeming results and also keep up with development of that external program for the variety of tests you'll run against it (which will grow, increasing the complexity of the fake). You'll of course have to do this yourself, because your vendor isn't going to do it for you. So now you have two problems.
- alquemist 6y agoFakes are not mocks. The fake is just another module. Assuming no updates, it is written once. Mocks are written 47 times, inconsistently, while focusing primarily on other tasks. If there are updates needed, better to fix them in one place than chasing 47 test code locations with inconsistent usages. Furthermore, these are external dependencies that can't be run in-process. If a dependency can be run in-process (aka library), there is no justification to ever mock it. I've even seen codebases that mock their own class B in order to test class A. Run the production code already. Ban mocking libraries.
- Nursie 6y agoTesting against external dependencies is only one form of testing. Unit testing with mocks is another form. One does not replace the other.
- barrkel 6y agoYou don't need me to get on that bandwagon. I almost despise mocks. Mocks usually over-specify implementations by setting up expectations of a specific implementation conversation rather than an outcome. They're painful to debug after refactoring implementation - they end up write-only - and they generally inhibit refactoring of the mocked API - often mocked instances of APIs outnumber production invocations. I try to encourage people to replace control flow with data flow where possible; messages, command objects rather than method calls; iterators, streams and consumers composed together, rather than loops. Data flow can normally be trivially redirected into a container, and if the data objects are simple inert immutable tuples, they're trivial to construct as inputs or assert against on outputs. Fakes are good too, better than mocks in most situations, since they are easier to refactor. Although, IME, integration tests, while slow and often brittle (especially if you have any async components and define failure conditions in terms of timeouts), have a significant upside in permitting large factoring while still being able to test a substantial amount of end result functionality (i.e. the stuff that matters, not implementation details).
- flukus 6y agoJava was full of AbstractSingletonBeanFactory classes long before unit testing became a thing, or at least before it was a widespread. I think IOC containers actually helped eliminate many of these abstraction overall, you need many less singletons and factories. > intercept and rewrite code at execution time I think "execution time" is one of the big issues, a lot of these patterns evolved from application server middleware like glassfish that would introspect and run packages and often provide services at runtime, sun and IBM were busy selling licenses and support for these applications servers so these abstractions were great for their bottom line. Take away the execution time requirement and injects mocks/fakes/stubs at compile time suddenly you don't need interfaces to make mocking easier, static methods are easy to mock and you probably don't need DI/IOC.
- cobbzilla 6y ago> Java was full of AbstractSingletonBeanFactory classes long before unit testing became a thing You're right that Java had some long class names to start with, but I think it was mostly Spring that introduced the PerhapsOverlyDetailedClassNamingConvention. Spring's introduction and subsequent wide adoption was a big driver of TDD's growth, which encouraged all those interfaces and mocks.
- watwut 6y agoHonestly, I like long names style of java. I dislike the code style where everyone is using shortcuts for everything.
- cobbzilla 6y agoI don't mind them if they're not too long. And if you can use more recent version of Java, the new `var` keyword is a nice verbosity reduction.
- hibbelig 6y agoI think the point of AbstractSingletonBeanFactory is not the length of the name, but what it does. More specifically, on how meta it is what it does. You can have a bean. Great. You can have a bean that's a singleton. Also great. Then you discover that you can have two different beans that are singletons, and you find that they have one thing in common: how the single instance is created and accessed. Each such singleton bean will have about five lines of code that deal with the single instance, and these five lines will be largely similar. So the Enterprise Java Architect sees that there is something to be abstracted, goes to town, and now you can have seven lines of code configuring the abstraction, so that you can avoid writing the five lines of boilerplate. Unfortunately, the seven lines of configuration will be largely similar for different singleton beans...
- Nursie 6y ago> leading to IoC containers, leading to lots of tools to assemble, intercept and rewrite code at execution time. Yeah, as a (mostly, currently) java programmer this is where the whole "framework" ecosystem loses me. Dependency injection - absolutely. Really useful, I can have a setup method that reads config from the environment or wherever, sets up various things and passes them in (you need an SSL context associated to these CAs? Great. And you need one that needs mutual authentication? OK then, here's one I made earlier) . Makes testing easier, and makes it obvious where the setup is being done and how this stuff hangs together. But 'full' IoC with a framework injecting stuff at runtime? Why? This just obfuscates as far as I can tell, and works around the language, making it harder to conceptualise and reason about. (Also yes, I don't subscribe to interface-itis).
- waheoo 6y ago> Unit testing, leading to dependency injection, leading to IoC containers, leading to lots of tools to assemble, intercept and rewrite code at execution time. The argument for static typing is often so that refactoring is easier and ide's work better. I always thought it was funny that the core reason for needing fancy ide's and large rename and refactor capabilities was that static typing and over abstraction in the first place.
- selfhoster11 6y agoJUnit and Mockito/PowerMock shouldn't have to exist, at all. The language should be feature-complete enough to provide built-in unit testing with mocking of everything under the sun (including static methods etc) - if you can provide APIs to operate on XML and obscure X.509 formats, you damn well can provide APIs and syntactic support to make modern unit testing possible and well-supported. As it stands, the necessity to relax members to `public` all over the place drives a giant bulldozer through any remaining delusions that Java is a language where actual OOP can be achieved.
- Hermel 6y agoThe problem is that unit testing as it is often done tears the object oriented design apart. The right way to do unit testing is to not test the internal state of an object in the test code, but to instead test it within the tested object using assertions. That way, running the unit test implicitly verifies the internal state of the object without having to expose it.
- vbezhenar 6y agoUsually people (ab)use default visibility modifier for unit testing. It's not public, so it's not visible from other packages, but unit tests usually reside in the same package, so you have full access to it. But I agree that Java could use better unit test and mocking support.
- an_opabinia 6y agoSplit modules are horrible.
- PicassoCTs 6y agoWhy use Interfaces for Unittests? Just extend and expose?