All my attempts to jump off OOP train break at exact same moment when I try to write a unit test.
Closure: or, simply use this monstrous component pattern when 70% of your code is boilerplate or hack into namespaces and override functions in them in runtime. And don’t forget to do it in right order!
JavaScript: yeah, simply re-define ‘require’ before importing dependencies in tests. Yeah, do it in right order.
Recent example - I was researching how to mock calls to functions in packages in go... Well, the best thing you can do is to have package private variable, assign function to it and use it throughout the code so you can swap it with mock/stub in a test.
There is none of that bs when I write Java or C#. I have a mechanism to decouple contracts from implementations - interfaces. I have mechanism to supply dependencies to modules - it’s called constructor parameters. I can replace implementations easily with mocks or stubs in tests without target even noticing that.
Can somebody provide me with an example of this kind of decoupling achieved in other paradigms _without_ hacking the runtime of a language or ugly tricks like in go case?
If you want testable code, the first step is to separate computations from effects. Most of your program should be immutable. Ideally you'd have a mostly functional core, used by an imperative shell.
Now to test a function, you just give it inputs, and check the outputs. Simple as that.
Oh you're worried that your function might use some other function, and you still want to test it in isolation? I said give it up. Instead, test that other function first, and when you're confident it's bug free (at least for the relevant use cases), then test your first function.
And in the rare cases where you still need to inject dependencies, remember that most languages support some form of currying.
That's a solid plan for acceptance testing and a great way to make sure you can never test diagnostic, recovery, rollback, and other something-abnormal-happened-here logic.
For small programs with few interfaces to worry about, that might be fine, but as you number of users go up, the odds go up that you'll be ensuring rollback bits get flipped when filesystems fail. None of that is simple to test without some sort of dependency injection or other heavyweight design pattern.
Because manually triggering an optimistic locking failure is a pain. Triggering one of those and a filesystem write failure at the same time is a whole pile of work compared to the mocking.
Go interfaces, Rust traits, and Haskell typeclasses and simple parameters do exactly what you ask. In fact, it's harder to mock/stub in OOP than it is in FP by the nature of FP making all dependencies always explicit. Being able to use a 'property' in the body of the functions means you're now relying on a side effect that will have to be magically mocked.
In OOP, type signatures tend to lie. Not so true for FP.
The problem with simple parameters is that I need to drag all of my low level stuff through all of the layers of the application.
I one was imagining a framework for Closure that would do something like that:
- you define a bunch of functions. Some of this functions depend on other of those functions.
- for dependent ones you define parameters and tag them somehow
- you call this functions without mentioning those tagged parameters
- during the startup the framework takes over and does partial application, generating functions with same names but without tagged parameters, so you kinda have your dependencies injected.
I don’t know if this sounds too crazy or too incorrect for Closure’s paradigm.
That's what composite values are for, combining related pieces of data such that they can be threaded through an application as a unit. State in FP is passed this way through an application.
Adding a framework is a premature abstraction that is likely a sign that you've got your types wrong.
Dependency injection is probably my favourite paradigm out there, especially in a statically typed language: for the reasons you mentioned, and for discoverability with an IDE (or ctags).
It is also a natural fit for “OOP”, or rather: classes. In a sense, it turns non-OO code into OO simply by storing the interfaces implementing the dependencies required by your code.
If that’s your only state, are you still OO? I’d say no. (Not as it’s commonly understood: mutable state.)
Hence, my proposed solution is: use classes & DI, but avoid (mutable) state (i.e. non dependencies).
I second this. I think that a class offers a simpler contract than a closure or a curried function, with the difference that you use "this" to refer to the enclosed objects. However, the same keyword can be used to hide state and that can lead to obscure or unexpected behaviors.
Sometimes while reading/writing modern PHP it feels like it's more functional than OOP. It's definitely easier for your code to be pure and simple instead of becoming a stateful mess. To make it a stateful mess you'd have to go against many modern best practices. I think I don't really understand what kind of Java monsters people have worked with in the past.
I will explicitly exclude legacy code from my previous statement, where globals, static calls with side effects, stateful objects and inheritance abuse were common.
Now if only I had all the advanced type system features :/.
You can see the Java AbstractInterfaceFactory creeping if you do Symfony development (which anyway is an impressive framework and very well run project).
Sometimes it feels Php devs have an inferiority complex and want to use as many design patterns as possible to feel like a real engineer. Two years ago suddenly everyone wants to add DDD or hexagonal design on top of the framework. Bye bye KISS, hello boilerplate everywhere.
I've done it well in C projects by tackling it at build time. Compile each test separately and use the dependencies of the test to decide what to link against, so if the test has "#include <mock-thing.h>" then that gets translated to link against mock-thing.o which implements all the functions from thing.h (the interface) plus some ways to manipulate the state. Basically compile time dependency injection. I think this is a fairly language independent way to handle things.
By relying on make for the heavy lifting the tests are completely incremental, only tests that depends on modified files get run. It also doesn't pollute the run time code with layers of indirection.
The same ideas still apply. Take a look at this explanation: http://vvgomes.com/javascript-dependency-injection/ -- suggestion for after you read the post: depending on your needs I'd recommend not using the default value as, from a dependency perspective, you end up tightly coupling the two. Hope it helps!
Closure: or, simply use this monstrous component pattern when 70% of your code is boilerplate or hack into namespaces and override functions in them in runtime. And don’t forget to do it in right order!
JavaScript: yeah, simply re-define ‘require’ before importing dependencies in tests. Yeah, do it in right order.
Recent example - I was researching how to mock calls to functions in packages in go... Well, the best thing you can do is to have package private variable, assign function to it and use it throughout the code so you can swap it with mock/stub in a test.
There is none of that bs when I write Java or C#. I have a mechanism to decouple contracts from implementations - interfaces. I have mechanism to supply dependencies to modules - it’s called constructor parameters. I can replace implementations easily with mocks or stubs in tests without target even noticing that.
Can somebody provide me with an example of this kind of decoupling achieved in other paradigms _without_ hacking the runtime of a language or ugly tricks like in go case?