2016-04-21 2 views
1

Hier ein Puzzlespiel. Ich habe ein lein-Plugin, das meinen clojure.test-Code manuell ausführt. Er deklariert eine dynamische Variable baseuri, auf die ich innerhalb meiner Tests zugreifen möchte. Ich werde mich ausziehen und den Code ändern, um sofort zur Sache zu kommen. Hier, innerhalb meines Plugins, habe ich eine Konfigurationsdatei, die die dynamische baseuri Variable erstellt und sie auf eine leere Zeichenfolge setzt.Verwenden einer dynamischen Variablenbindung in einem Leiningen-Plugin

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

Eine Aufgabe, aus dem Plugin setzt die dynamischen baseuri Variable und führt Tests mit 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) 
)) 

Und Innenseite meines clojure.test Ich versuche, die BaseUri Variable zu verwenden, aber die Bindung hält nicht. Sein Wert ist, was ich ursprünglich baseuri erklärt (eine leere Zeichenkette)

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

Ich bin wirklich hier bei einem Verlust. Irgendwelche Erklärungen dafür, warum dies der Fall sein würde? Ich hatte die genaue gleichen Code funktioniert, wenn es als eigenständige Anwendung geschrieben wurde. Aber als ich es auf ein lein Plugin portierte, tauchte dieses bindende Problem auf. Ein ernsthafter Kopfkratzer.

EDIT

Ich erhalte Kommentare über die globale dynamische Variablen. Obwohl ich der Meinung bin, dass sich die Frage nicht rechtfertigen muss, werde ich Hintergrundinformationen liefern, in der Hoffnung, dass andere dem Programmierer mehr Sympathie entgegenbringen.

Der Zweck dieses Plugins ist es, skriptfähige und parallele Ausführung von e2e-Tests auf Remote-Service-Provider wie Saucen zu ermöglichen. Dies ist nicht möglich mit Vanille clojure.test. Sie können die Konfiguration nicht über die Befehlszeile mit lein test übergeben. Außerdem verwende ich clj-webdriver für E2e-Tests und es ist nicht möglich, Testsuiten mehrmals parallel auf verschiedenen Plattformen mit clojure.test zu laufen. Sowohl clojure.test als auch clj-webdriver erzwingen einen nicht funktionalen Code und erfordern, dass ich einige globale dynamische Variablen verwende. (Diese beiden Projekte verwenden Globals und sogar Namespace-Metadaten, ihre Lösungen zu zerhacken.)

Aber das Festigkeit, um eine projektweite baseuri konfiguriert werden vom project.clj und auch zu ermöglichen - da dies zu verstehen ist sein scriptable - Ich benötige, dass baseuri mit einem Befehlszeilenargument zum Plugin überschrieben werden, wenn Sie es ausführen, muss ich die Variable aus dem Plugin setzen. Da die Variable vom Plugin gesetzt werden muss (von der Kommandozeile aus geparst) und ich keine Parameter an deftest übergeben kann, ist meine Option eine dynamische Bindung. Für die funktionalen Puristen ist dies nur ein weiteres Beispiel dafür, dass das wirkliche Leben unordentlich ist.

Ich habe den Code bearbeitet, um in einer grundlegenden Weise zu zeigen, wie die clojure.test-Fälle ausgeführt werden. Ich gebe einfach den Namespace, der ausgeführt werden soll, an die Methode clojure.test/run-tests weiter.

Das eigentliche Plugin ist ein paar hundert Zeilen Code mit Abstraktionen, Kommandozeilenanalyse, Namespace-Suche und eine ganze Reihe mehr. Aber was Sie hier sehen, sind die einzigen wichtigen Teile. Es werden keine Threads gestartet. Die Clojure-Testfälle heißen innerhalb des Bindungsbereichs und das alles funktioniert, wenn sie in einer eigenständigen Anwendung ausgeführt werden.Wenn der eigenständige e2e-Test-Runner in eine lein-Plugin-Aufgabe eingefügt wurde, scheint die Bindung irgendwie aus dem Rahmen zu fallen, obwohl sich der Code nicht geändert hat.

+0

Können Sie uns zeigen, welcher Code genau an Stelle Ihres Kommentars steht ';; rennt clojure.test code here'? –

+0

Ich habe meine Antwort bearbeitet, um zu zeigen, wie die clojure.test-Testfälle laufen. –

Antwort

1

Ich stimme zu, dass die Implementierung clojure.test nicht optimal ist, wenn es um die Parametrierung Ihrer Tests geht.

Ich bin mir nicht sicher, warum Ihr binding Formular nicht funktioniert - Ich habe den Code in clojure.test überprüft und ich kann nicht sehen, was falsch sein könnte. Ich würde überprüfen, ob:

  • die Tests im selben Thread ausgeführt werden sollen wie die binding hergestellt wird (vielleicht könnte man den Faden Name/id in Ihrem Plugin Anmeldung hinzufügen und in Ihren Tests)

  • verschiedene Klassenlade verursachen, dass Ihr Plugin-Namespace und seine globale dynamische Variable ist tatsächlich geladen und definiert zweimal

ich habe noch eine Idee (und ich möchte wirklich nicht Ihre Lösung zu kritisieren, nur versuchen, alternative Lösungen zu finden:)): Ihr Problem besteht darin, eine globale Konfigurationsoption an Ihren Testcode aus externen Quellen wie der Testskriptkonfiguration zu übergeben. Haben Sie darüber nachgedacht, diese als Umgebungsvariablen zu übergeben? Sie können sie einfach mit (System/getenv "baseuri") oder environ lesen.

+0

Vielen Dank für mehr Hintergrund und Details in Ihrer Frage. Ich habe meine Antwort bearbeitet. Es tut mir leid, wenn Sie meine vorherige Antwort nicht mitfühlend fanden, ich versuchte nur, hilfsbereit zu sein. –

+0

Das sind ausgezeichnete Ideen! Vielen Dank! –

+0

Sie hatten Recht. In meiner 'project.clj' Datei habe ich das Plugin zum' 'plugins' Map Key hinzugefügt.Um die Plugin-Konfiguration aus der Anwendung heraus zu "erzwingen", muss ich das Plugin auch im Map-Schlüssel ': dependencies' deklarieren. So wird es doppelt geladen, genau wie du dachtest. Danke, Piotrek, für deine Zeit hier. Sehr, sehr geschätzt. –

0

Vielleicht haben Sie eine dynamische Var aus ganz bestimmten Gründen, aber, wie Sie nicht so explizit angeben, ich hier eine Aufnahme machen.

Vermeiden Sie dynamisches Rebinding von vars. Im besten Fall meiden Sie den globalen Zustand, definieren Sie stattdessen Ihre Funktionen neu, um die Basis als Parameter zu verwenden.
Oder refaktorieren Sie Ihre Anwendung so, dass Sie statische Vars gar nicht benötigen, wie Sie es gerade haben.

EDIT Meine Vermutung ist, dass Ihre Funktionen:

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

nicht in irgendeiner Weise verbunden sind. Zumindest sehe ich nicht, wie sie sein sollten. Sie führen einen Test aus und drucken einige andere Var, ohne sie erneut zu binden.
Vielleicht könnten Sie einen Link zu einem Repo zu einem minimalen reproduzierbaren Testfall hinzufügen, um es besser zu verstehen?

+0

Hey sveri, stimme ich zu. Leider ist es aufgrund der enormen Einschränkungen von 'clojure.test' nicht möglich, Parameter an Tests zu übergeben, die mit' deftest' geschrieben wurden. Ich habe festgestellt, dass sehr wenig von "clojure.test" eine funktionale Programmierung ermöglicht, die meine Hände hinter meinem Rücken verbindet. Wenn "deftest" erlaubte Argumente wären, würde ich gesetzt werden. –

+0

Es ist schwer zu sagen. still: 1. rekonfiguriere deine Anwendung, sie sieht einfach falsch aus 2. 'binding' bindet nur in seinem lokalen Bereich, sobald du das' binding'cope 'baseuri' verlässt, wird wieder' '' 'sein. – sveri

+0

1. Dies ist nicht meine App, es ist nur eine Zusammenfassung des Problems. 2. Ich würde gerne Rückmeldungen darüber erhalten, warum dies nicht im Rahmen des Verbindlichen ist. Das wäre hilfreich. –

Verwandte Themen