简体   繁体   中英

Using a dynamic variable binding in a leiningen plugin

I've got a lein plugin that manually runs my clojure.test code. It declares a dynamic variable baseuri that I wish to access from within my tests. I'll strip out and change the code to get straight to the point. Here, inside of my plugin, I have a config file that creates the dynamic baseuri variable and sets it to a blank string.

;; myplugin
;; src/myplugin/config.clj
(ns leiningen.myplugin.config)    
(def ^:dynamic baseuri "")

A task from within the plugin sets the dynamic baseuri variable and runs tests with clojure.test:

;; src/myplugin/runtests.clj
(ns leiningen.myplugin.runtests
      (:require [leiningen.myplugin.config :as config]
                [clojure.test]
                [e2e.sometest]))

(defn run [project]
  (binding [config/baseuri "https://google.com/"]
    (println config/baseuri) ;; <-- prints google url
    ;; run clojure.test test cases from e2e.sometest namespace
    ;; This will call the `sampletest` test case
    (clojure.test/run-tests e2e.sometest)
  ))

And inside of my clojure.test I try to use the baseuri variable but the binding doesn't hold. It's value is what I originally declared baseuri to be (an empty string)

;; tests/e2e/sometest.clj
(ns e2e.sometest
  (:require [leiningen.myplugin.config :as config]))

(deftest sampletest
  (println config/baseuri))  ;; <-- Prints an empty string instead of google url

I've edited the code to show in a basic manner how the clojure.test cases are run. I simply pass in the namespace I want to be run to the clojure.test/run-tests method.

I agree that clojure.test implementation is not optimal when it comes to parameterising your tests.

I am not sure why your binding form doesn't work - I have checked the code in clojure.test and I cannot see what could be wrong. I would check if:

  • the tests get executed in the same thread as the binding is established (maybe you could add logging the thread name/id in your plugin and in your tests)

  • different class loaders causing that your plugin namespace and its global dynamic variable is actually loaded and defined twice

I have one more idea (and I really don't want to criticise your solution, just trying to find alternative solutions :)): your problem is to pass a global configuration options to your code under test from external sources like test scripts configuration. Have you thought about passing them as environment variables? You could easily read them using (System/getenv "baseuri") or environ .

Maybe you have a dynamic var for very specific reasons, but, as you do not state so explicitly, I take a shot here.

Avoid dynamic rebinding of vars. At its best case, avoid global state at all, instead redefine your functions to take the baseuri as a parameter.
Or refactor your application to not have the need for static vars at all, like you have it right now.

EDIT My guess is that your functions:

(defn run [project]
  (binding [config/baseuri "https://google.com/"]
    (println config/baseuri) ;; <-- prints google url
    ;; runs clojure.test code here …
  ))

(deftest sampletest
  (println config/baseuri))

are not connected in any way. At least I dont see how they should be. You are running a test and print some other var without rebinding it.
Maybe you could add a link to a repo to a minimal reproducable testcase to understand it better?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM