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.
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.
In OOP, type signatures tend to lie. Not so true for FP.
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.
Adding a framework is a premature abstraction that is likely a sign that you've got your types wrong.
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).
Curious to hear others’ thoughts on this, though.
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 :/.
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.
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.
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?