Reputation: 71
I have three services in a Clojure application:
Service A
: interacts with a third-party API, causing potentially dangerous and irreversible changes within the external service (may not be written by me)Service B
: consumes Service A to accomplish its goalsService C
: consumes Service B to accomplish its own goalsI need to write integration tests for Service C that do not call out to the third-party, as they provide neither a set of testing endpoints nor a way to reverse my changes.
The two options I see are:
Pass called functions into Service C, like how one would inject service instances in an OOP language. This causes a lot of additional overhead when actually using Service C.
Mocking the dangerous functions in Service A using with-redefs-fn
. This requires knowing implementation details of services that I may not control.
I am somewhat new to testing in functional languages, so it's more than possible that I'm missing something obvious.
Upvotes: 0
Views: 249
Reputation: 1665
If I understand your use-case correctly, I'd avoid calling out to Service A in my integration tests for Service C by mocking the whole Service A with clj-fake-http. I've used that library very happily in one of my work projects and can highly recommend it.
This assumes that Service A is actually a different process that you're talking to. Should Service A live in the same process as Service C you might as well mock away the calls to the 3rd party API that Service A does.
Upvotes: 1
Reputation: 3346
If you know some details about the implementation of the services, and those are mostly written in Java, you can use proxy
to create an alternate implementations of the classes or interfaces that you want to constraint during the tests.
An example would look like:
(let [proxy-a (proxy [com.dangerous.ServiceA] []
(launchMissiles [x y z]
(log/info "Phew! we didn't fire a missile during tests!")))
proxy-b (proxy [com.example.ServiceB] []
(callServiceA [x y z]
(.launchMissiles proxy-a x y z)))
proxy-c (proxy [com.example.ServiceC] [x]
(callServiceB [x]
(.callServiceB proxy-b x 1 2)))]
;; Perform a test on the outer proxy with:
(.callServiceB proxy-c "testing!")
More details here: https://clojuredocs.org/clojure.core/proxy
Upvotes: 1
Reputation: 29958
Use option #2, but with-redefs
(there is almost never a need for with-redefs-fn
. See
Example:
(ns http)
(defn post [url] ; dummy fn for testin
{:body "Hello world"})
(ns app
(:require [clojure.test :refer [deftest is run-tests]]))
(deftest is-a-macro
(with-redefs [http/post (fn [url] {:body "Goodbye world"})]
(is (= {:body "Goodbye world"} (http/post "http://service.com/greet")))))
(run-tests) ;; test is passing
Upvotes: 0