¿Cómo puedo obtener Clojure: pre&: post para reportar su valor fallido?


(defn string-to-string [s1] 
  {:pre  [(string? s1)]
   :post [(string? %)]}
  s1)

Me gustan las condiciones :pre y :post, me permiten averiguar cuándo he puesto "clavijas cuadradas en agujeros redondos" más rápidamente. Tal vez está mal, pero me gusta usarlos como una especie de verificador tipo pobre hombre. Sin embargo, esto no es filosofía, es una pregunta simple.

Parece en el código anterior que debería ser fácilmente capaz de determinar que s1 es un argumento de función en la condición :pre. Similarmente, % en la condición :post es siempre el valor de retorno de la función.

Lo que me gustaría es imprimir el valor de s1 o % cuando cualquiera de estas condiciones respectivas fallan dentro del AssertionError. Así que tengo algo como

(string-to-string 23)

AssertionError Assert failed: (string? s1) 
(pr-str s1) => 23 

Con el AssertionError conteniendo una sola línea para cada variable que fue identificada como de la lista de argumentos de la función y que fue referenciada en la prueba fallida. También me gustaría algo similar cuando el valor devuelto de la función falla la condición :post.

Esto lo haría trivial para detectar rápidamente cómo hice un mal uso de una función al intentar diagnosticar desde el AssertionError. Al menos me haría saber si el valor es nil o un valor real (que es el error más común que hago).

Tengo algunas ideas de que esto podría hacerse con una macro, pero me preguntaba si había alguna forma segura y global de redefinir básicamente lo que (defn y (fn y amigos hacen para que :pre y :post también impriman los valores que conducen a que la prueba falle.

Author: Stephen Cagle, 2014-07-19

3 answers

Usted podría envolver su predicado con el is macro de clojure.test

(defn string-to-string [s1] 
  {:pre  [(is (string? s1))]
   :post [(is (string? %))]}
 s1)

Entonces obtienes:

(string-to-string 10)
;FAIL in clojure.lang.PersistentList$EmptyList@1 (scratch.clj:5)
;expected: (string? s1)
;actual: (not (string? 10))
 26
Author: optevo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-19 07:55:30

@octopusgrabbus insinuó esto al proponer (try ... (catch ...)), y usted mencionó que eso podría ser demasiado ruidoso, y todavía está envuelto en una afirmación. Una variante más simple y menos ruidosa de esto sería una sintaxis simple (or (condition-here) (throw-exception-with-custom-message)), como esta:

(defn string-to-string [s1] 
  {:pre  [(or (string? s1)
              (throw (Exception. (format "Pre-condition failed; %s is not a string." s1))))]
   :post [(or (string? %)
              (throw (Exception. (format "Post-condition failed; %s is not a string." %))))]}
  s1)

Esto esencialmente le permite usar condiciones previas y posteriores con mensajes de error personalizados the las condiciones previas y posteriores todavía se comprueban como lo harían normalmente, pero su excepción personalizada se evalúa (y, por lo tanto, se lanza) antes de que el AssertionError pueda suceder.

 12
Author: Dave Yarwood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-21 21:18:02

Algo como abajo donde clojure spec está explicando el problema? Esto lanzará un error de aserción que puede detectar.

 (defn string-to-string [s1] 
  {:pre [ (or (s/valid?  ::ur-spec-or-predicate s1) 
              (s/explain ::ur-spec-or-predicate s1)]}
  s1)
 2
Author: Manoj Arya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-05 12:48:08