Использование динамической привязки переменных в плагине leiningen

Здесь загадка. У меня есть плагин lein, который вручную запускает мой код clojure.test. Объявляет динамическую переменную baseuri что я хочу получить доступ из моих тестов. Я раздену и изменю код, чтобы перейти прямо к делу. Здесь, внутри моего плагина, у меня есть файл конфигурации, который создает динамический baseuri переменная и устанавливает его в пустую строку.

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

Задача из плагина устанавливает динамику baseuri переменная и запускает тесты с 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)
  ))

И внутри моего clojure.test я пытаюсь использовать переменную baseuri, но привязка не выполняется. Это ценность, это то, что я первоначально объявил baseuri быть (пустая строка)

;; 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

Я действительно в растерянности здесь. Любые объяснения, почему это будет иметь место? У меня был точно такой же код, когда он был написан как отдельное приложение. Но когда я портировал его на плагин lein, эта проблема связывания всплыла. Серьезный скребок головы.

РЕДАКТИРОВАТЬ

Я получаю комментарии об использовании глобальных динамических переменных. Хотя я чувствую, что вопрос не нуждается в самооправдании, я предоставлю предысторию в надежде, что другие будут более сочувствующими кодеру.

Назначение этого плагина - разрешить выполнение сценариев и параллельное выполнение тестов e2e на удаленных поставщиках услуг, таких как программные блоки. Это невозможно при использовании ванили clojure.test, Вы не можете перейти в конфигурацию через командную строку с lein test, Также я использую clj-webdriver для тестирования e2e и невозможно запускать наборы тестов несколько раз параллельно на разных платформах с clojure.test, И то и другое clojure.test а также clj-webdriver принудительно использовать нефункциональный стиль кода и требовать, чтобы я использовал пару глобальных динамических переменных. (Оба этих проекта используют глобальные и даже метаданные пространства имен для взлома своих решений.)

Но это выдерживает, для того, чтобы позволить всему проекту baseuri быть настроенным из project.clj, а также - так как это предназначено для сценариев - я требую, чтобы baseuri быть переопределенным с аргументом командной строки для плагина при его запуске, я должен установить переменную из плагина. Поскольку переменная должна быть установлена ​​плагином (анализируется из командной строки), и так как я не могу передать какие-либо параметры в deftestМой единственный вариант - использовать динамическое связывание. Так что для функциональных пуристов это просто еще один пример того, что в реальной жизни грязно.

Я отредактировал код, чтобы в основном показать, как выполняются случаи clojure.test. Я просто передаю пространство имен, которое я хочу запустить в clojure.test/run-tests метод.

Фактический плагин представляет собой несколько сотен строк кода с абстракциями, разбором командной строки, поиском в пространстве имен и множеством других. Но то, что вы видите здесь, являются единственными важными частями. Нет темы запущены. Тестовые случаи clojure вызываются внутри области привязки, и все это работает при запуске в автономном приложении. Когда автономный тестер e2e был включен в задачу плагина lein, привязка каким-то образом выпала из области видимости, хотя код не изменился.

2 ответа

Решение

Я согласна с тем что clojure.test реализация не является оптимальной, когда речь идет о параметризации ваших тестов.

Я не уверен, почему ваш binding форма не работает - я проверил код в clojure.test и я не вижу, что может быть не так. Я бы проверил, если:

  • тесты выполняются в том же потоке, что и binding установлено (возможно, вы могли бы добавить запись имени / идентификатора потока в вашем плагине и в ваших тестах)

  • различные загрузчики классов, в результате чего пространство имен вашего плагина и его глобальная динамическая переменная фактически загружаются и определяются дважды

У меня есть еще одна идея (и я действительно не хочу критиковать ваше решение, просто пытаюсь найти альтернативные решения:)): ваша проблема заключается в передаче глобальных параметров конфигурации в тестируемый код из внешних источников, таких как настройка тестовых сценариев. Задумывались ли вы о передаче их в качестве переменных среды? Вы можете легко прочитать их, используя (System/getenv "baseuri") или окружающая среда.

Может быть, у вас есть динамический var по очень конкретным причинам, но, поскольку вы не указали это явно, я сделаю снимок здесь.

Избегайте динамического переплета переменных. В лучшем случае вообще избегайте глобального состояния, вместо этого переопределите ваши функции, чтобы принять baseuri в качестве параметра.
Или рефакторинг вашего приложения, чтобы вообще не нуждаться в статических переменных, как у вас сейчас.

РЕДАКТИРОВАТЬ Я думаю, что ваши функции:

(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))

никак не связаны. По крайней мере, я не понимаю, как они должны быть. Вы запускаете тест и печатаете какой-то другой var, не привязывая его.
Может быть, вы могли бы добавить ссылку на репозиторий в минимальный воспроизводимый тестовый набор, чтобы лучше это понять?

Другие вопросы по тегам