2

I've a luminus project with some simple compojure-api routes. I've added carmine to communicate with a redis server, using the wcar* macro (defined in services.clj) to make calls to it, and everything works fine.

Now I'm trying to add some tests, but seems that the redis connection doesn't works properly during them, because I'm receiving this error with lein test:

ERROR Carmine connection error

clojure.lang.ExceptionInfo: Carmine connection error {}

Since it's working in dev e prod environments, I think that is something related to a missing env load in the test environment, but I didn't find a way to solve it.

These are relevant parts of the code in use:

test.clj

(ns app.test.handler
  (:require [clojure.test :refer :all]
            [ring.mock.request :refer :all]
            [app.handler :refer :all]))

(deftest test-app
  (testing "redis ping"
    (let [response ((app) (request :get "/api/redis-ping"))]
      (is (= 200 (:status response))))))

services.clj

(ns app.routes.services
  (:require [ring.util.http-response :refer :all]
            [compojure.api.sweet :refer :all]
            [schema.core :as s]
            [app.config :refer [env]]
            [clojure.tools.logging :as log]
            [mount.core :refer [defstate]]
            [taoensso.carmine :as car :refer (wcar)]))

(defmacro wcar* [& body] `(car/wcar
                           {:spec {:host (:redis-host env) :port (:redis-port env)}}
                           ~@body))

(defapi service-routes
     (context "/api" []
           :tags ["myapi"]

         (GET "/redis-ping" []
               :return String
               :summary "A redis client test."
               (ok (wcar* (car/ping "hello"))))))

handler.clj

(ns app.handler
  (:require [compojure.core :refer [routes wrap-routes]]
            [app.routes.services :refer [service-routes]]
            [compojure.route :as route]
            [app.env :refer [defaults]]
            [mount.core :as mount]
            [app.middleware :as middleware]))

(mount/defstate init-app
                :start ((or (:init defaults) identity))
                :stop  ((or (:stop defaults) identity)))

(def app-routes
  (routes
    #'service-routes
    (route/not-found
      "page not found")))


(defn app [] (middleware/wrap-base #'app-routes))

Profiles.clj

{:profiles/dev  {:env {:redis-host "127.0.0.1" :redis-port 6381}}
:profiles/test {:env {:redis-host "127.0.0.1" :redis-port 6381}}}

Config.clj

(ns app.config
  (:require [cprop.core :refer [load-config]]
            [cprop.source :as source]
            [mount.core :refer [args defstate]]))

(defstate env :start (load-config
                       :merge
                       [(args)
                        (source/from-system-props)
                        (source/from-env)]))

SOLUTION

Add a text fixture with the mount/start command that's executed before tests.

Add to test.clj:

(defn my-test-fixture [f]
  (mount/start)
  (f))

(use-fixtures :once my-test-fixture)
Community
  • 1
  • 1
lifeisfoo
  • 15,478
  • 6
  • 74
  • 115

1 Answers1

0

You are using mount to manage your application state lifecycle. I think you are not calling (mount/start) in your tests thus your app.config/env state is not initialized properly. On the other hand when you start your application (mount/start) is probably called and thus it's working correctly.

Piotrek Bzdyl
  • 12,965
  • 1
  • 31
  • 49
  • You're right. Trying to log `env` value at the beginning of a test results in: `;; Testing app.test.handler ;; ... #object[mount.core.DerefableState 0x6bf86b4a {:status :ready, :val #object[mount.core.NotStartedState 0x59eac843 '#'app.config/env' is not started (to start all the states call mount/start)]}]`. I'll update the question with the working code. – lifeisfoo Aug 14 '16 at 12:08
  • I think you should also call `(mount/stop)` after your tests in the fixture to have a proper test isolation (preferably in `finally` block). – Piotrek Bzdyl Aug 14 '16 at 12:13