I remember reading an article about how Ruby doesn't really need DI or DI frameworks because the classes are open. As a result, you can simply rewrite a dependency's constructor so that it returns a fake object.
I'm very new to Clojure and functional programming. I'm wondering if Clojure needs dependency injection or it can forgo it for similar/other reasons. Here's a concrete example to work with (feel free how to point out how my design is non-idiomatic of Clojure):
Imagine you're developing a web crawler/spider. It needs to traverse a webpage you've downloaded. This is an action with side-effects. The webpage could change on every query, your internet connection could cut out, etc. It finds all the links on the webpage, visits each one, and then traverses it in the same way.
Now, you want to write a test that mocks out the http client so it returns a hard coded string response instead. How do you call the program's -main
in a test and prevent it from using the real http client?
Dependency injection helps to develop testable code, allowing developers to write unit tests easily. You can use mock databases with dependency injection, and test your application without affecting the actual database.
Dependency Injection is a technique to make the classes in Object Oriented Programming easier to test and configure. Instead of a class instantiating its own concrete implementations, it instead has them injected into it. In Functional Programming, that's a fancy way of saying “calling a function with parameters”.
In software engineering, dependency injection is a design pattern in which an object or function receives other objects or functions that it depends on. A form of inversion of control, dependency injection aims to separate the concerns of constructing objects and using them, leading to loosely coupled programs.
Dependency Injection (or DI for short) is a way of constructing your application so that you are not creating dependencies locally, but instead are being provided those dependencies from somewhere else in your application.
The with-redefs
macro in clojure.core is very useful for stubbing out functions.
Here's a short REPL session to demonstrate this:
user=> (defn crawl [url]
#_=> ;; would now make http connection and download content
#_=> "data from the cloud")
#'user/crawl
user=> (defn -main [& args]
#_=> (crawl "http://www.google.com"))
#'user/-main
user=> (-main)
"data from the cloud"
user=> (with-redefs [crawl (fn [url] "fake data")]
#_=> (-main))
"fake data"
Since a Clojure program is composed (mostly) of functions, not objects, dynamic rebinding of functions replaces a good deal of what a DI framework would do for testing purposes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With