Hey, I know there's a new edition of "Programming Clojure" comin' up, is will there be another "Clojure applied" or if there's something equivalent out there?
The magic 8 ball says unlikely. Pragmatic is going through business changes and is unlikely to be publishing new books in the short term (programming Clojure 4th ed is essentially done and will get out ahead of that).
But, you never know
At least, some demand is there. I would buy it.
How to do mocking in clojure? I have a function where I call a service that gets some info from an external service, but when I test this fn, I'd like to mock this call so it return some static data and not do a real call
If you use spec you can stub or replace in instrument: https://clojure.github.io/spec.alpha/clojure.spec.test.alpha-api.html#clojure.spec.test.alpha/instrument
Are you writing unit tests or integration tests? If you're writing unit tests, is it an option to write a (pure) function that takes the result of the external service call, then test that function? If you're writing integration tests and if it's a HTTP request you're making, you could consider faking the HTTP server instead of mocking. Here's an example that uses http-kit and reitit:
(defn ^:private wrap-json-response [handler]
(fn [request]
(-> (handler request)
(assoc-in [:headers "Content-Type"] "application/json")
(update :body json/coll->json))))
(defmacro ^:private with-http-server
[http-server-options routes & body]
`(let [http-server# (http-server/run-server
(ring/ring-handler
(ring/router
~routes
{:data {:middleware [[wrap-json-response]]}}))
(merge ~http-server-options {:legacy-return-value? false}))]
(try
(do ~@body)
(finally
(http-server/server-stop! http-server#)))))
(deftest t
(with-http-server {:port 8000} [["/user/:id" {:post (constantly {:status 200 :body {:id 1}})}]]
(do-things)))
The problem with mocking in integration tests, in my view, is that it ties your tests to implementation details and makes the function you're mocking cumbersome to change, because a ton of your tests rely on it. But YMMV, of course.It's easy to confuse OOP and modularity. OOP combines state and methods that operate on that state into opaque instances that are not data-structures, but complex object constructs that show up as object[memory-addrress] and so on. Then it layers things on top of that like inheritance of both state and/or methods, overrides, and so on. Even then, it's not necessarily bad, when used for what it's good at, such as simulations. You can see that in Clojure's rational: • Born of simulation, now used for everything, even when inappropriate • Mutable stateful objects are the new spaghetti code • Inheritance is not the only way to do polymorphism • Write Java in Java, consume and extend Java from Clojure Polymorphism is good, if it's not done through inheritance, it's not OOP. That's how protocols work in Clojure, non OOP-based polymorphism. Grouping functions is good, as long as you don't tie it up with mutable state. And so on.
That said, I think you can organize your code to not need to mock IO at all, and while what people said, injecting a mock is fine, I think it's even better to split out all the IO code that you're trying to test from that function into a pure function.
(defn prepare-request
...)
(defn extract-response []
...)
(defn call-foo! [request]
...) ;; returns response
Now you don't even need to mock call-foo!. Just integ test it instead. And unit test the rests.The most basic approach is with-redefs but using Component and providing test doubles is more scalable solution in the long run/bigger codebase
Have the external service be an argument and then pass in a function or reified protocol that mimics in in whatever behavior you want
concrete example:
(defprotocol FruitClient
(list-fruits [_ query])
(defn real-fruit-client
[{:keys [base-url]]
(let [client (http/client ...)]
(reify FruitClient
(list-fruits [_ query]
(http/get client ...)))))
(defn mock-fruit-client
[]
(reify FruitClient
(list-fruits [_ _]
[])))
There's a few different Clojure mocking libraries, like https://github.com/bguthrie/shrubbery. https://www.clojure-toolbox.com/ lists a few.
Yeah protocols don't strike me as OOP. The comparison of named data fields as slots vs keys comes to mind here. One pattern I like to use is passing around a "context" map of functions and other dependencies. But as that map grows with your system, you really start to appreciate the power of protocols.
Long ago, I made this lib: https://github.com/igrishaev/mockery It still can help perhaps
@dpsutton not that it is bad but this sounds like a very OOP-y approach, I thought Clojure would lean another way. Maybe taking in data from a function where the data can be swapped/mocked during testing?
maybe a bit. It’s easy to think of it as an implementation of an interface. There was a clojure coder named tim baldridge who made an interesting argument for me that a protocol is just a collection of functions. if you have a few that work in concert, make it a protocol and then it’s easy to swap them out. I’m not sure how functional or oopy it is. But i often really like the pattern. Especially when i want an implementation that is stateful; like it throws an error on the second invocation, etc
@nathanfurnal that was a trap I fell into when working with Clojure early on - I was also avoiding OOP-like constructs and language features, but in the end it only hinders productivity. Plus, Clojure is designed to embrace the host (JVM) and there's nothing wrong with some objects, methods etc, especially when state is involved
I'm not saying it's bad! I'm just curious what strategy ppl come up with. I'm in the process of testing an external service at work (not in Clojure though) and I've been considering different strategy. For easy things like testing a request, you can just pass data to a function but anytime you have some state on the provider's part, an object seems like the better alternative.
Exactly. It boils down to "it depends" and picking what makes sense for your codebase
But I want nice answers that work every time! 😁
Okay here is another pattern
(defn list-fruits
[client]
((::list-fruits client))
(defn get-fruit
[client {:keys [id]}]
((::get-fruit client) {:id id}))
Just pass a map of functions with all the state in closures(defn real-fruit-client
[opts]
(let [client (http/client ...)]
{::get-fruit (partial #'real-get-fruit client)
::list-fruits (partial #'real-list-fruits client)}))the only downside is that you can't really access the state because its in a lexical closure. to solve that you could attach the functions as metadata on a map of the state
(defn real-fruit-client
[opts]
(let [client (http/client ...)]
(with-meta
{:client client}
{::get-fruit #'real-get-fruit
::list-fruits #'real-list-fruits})))and have the functions read them off the metadata
That’s basically a protocol
at which point we've reinvented extend-via-metadata protocols
the difference is that this function implementation is done for you by the protocol:
(defn list-fruits
[obj]
((::list-fruits (meta obj)) obj))
And a protocol gives a name to the aggregate + extends it to nominal types ... etc.but maybe you don't want that stuff - there is an alternate dimension non-hosted clojure that doesn't even touch nominal aggregates