Использование динамической привязки переменных в плагине 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, не привязывая его.
Может быть, вы могли бы добавить ссылку на репозиторий в минимальный воспроизводимый тестовый набор, чтобы лучше это понять?