Подтвердить что ты не робот

Значительное сообщение об ошибке для Clojure. Проверка проверки в: pre

Я использовал последние дни, чтобы углубиться в clojure.spec в Clojure и ClojureScript.

До сих пор я нахожу это наиболее полезным, использовать спецификации в качестве охранников в :pre и :post в публичных функциях, которые полагаются на данные в определенном формате.

(defn person-name [person]
  {:pre [(s/valid? ::person person)]
   :post [(s/valid? string? %)]}
  (str (::first-name person) " " (::last-name person)))

Проблема с этим подходом заключается в том, что я получаю java.lang.AssertionError: Assert failed: (s/valid? ::person person) без какой-либо информации о , что точно не соответствовало спецификации.

Есть ли у кого-нибудь идея как получить лучшее сообщение об ошибке в :pre или :post охранниках?

Я знаю о conform и explain*, но это не помогает тем сторонам :pre или :post.

4b9b3361

Ответ 1

В новых альфах теперь есть s/assert, который может использоваться для утверждения, что входное или возвращаемое значение соответствует спецификации. Если оно действительное, возвращается исходное значение. Если это неверно, с результатом объяснения возникает ошибка утверждения. Утверждения могут быть включены или выключены и могут быть необязательно исключены из скомпилированного кода целиком, чтобы иметь влияние на производство 0.

(s/def ::first-name string?)
(s/def ::last-name string?)
(s/def ::person (s/keys :req [::first-name ::last-name]))
(defn person-name [person]
  (s/assert ::person person)
  (s/assert string? (str (::first-name person) " " (::last-name person))))

(s/check-asserts true)

(person-name 10)
=> CompilerException clojure.lang.ExceptionInfo: Spec assertion failed
val: 10 fails predicate: map?
:clojure.spec/failure  :assertion-failed
 #:clojure.spec{:problems [{:path [], :pred map?, :val 10, :via [], :in []}], :failure :assertion-failed}

Ответ 2

Я думаю, что идея заключается в том, что вы используете spec/instrument для проверки ввода и вывода функций, а не условий предварительной и последующей.

Вот хороший пример в нижней части этого сообщения в блоге: http://gigasquidsoftware.com/blog/2016/05/29/one-fish-spec-fish/. Краткое описание: вы можете определить спецификацию для функции, включая как входные, так и возвращаемые значения, используя клавиши: args и: ret (таким образом, заменяя как условия pre, так и post), с помощью spec/fdef, измерить его, и вы получите результат, аналогичный используя explain, когда он не отвечает спецификации.

Минимальный пример, полученный из этой ссылки:

(spec/fdef your-func
    :args even?
    :ret  string?)


(spec/instrument #'your-func)

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

Подробнее в официальном руководстве: https://clojure.org/guides/spec --- см. раздел "Функции функции".

Ответ 3

Без учета того, следует ли использовать предварительные и пост-условия для проверки аргументов функции, есть способ распечатать несколько более четкие сообщения из условий pre и post, завернув ваш предикат с помощью clojure.test/is, как это предлагается в ответе ниже:

Как я могу получить Clojure: pre и: post, чтобы сообщить о своем неудачном значении?

Итак, ваш код может выглядеть так:

(ns pre-post-messages.core
  (:require [clojure.spec :as s]
            [clojure.test :as t]))

(defn person-name [person]
  {:pre [(t/is (s/valid? ::person person))]
   :post [(t/is (s/valid? string? %))]}
  (str (::first-name person) " " (::last-name person)))

(def try-1
  {:first-name "Anna Vissi"})

(def try-2
  {::first-name "Anna"
   ::last-name "Vissi"
   ::email "[email protected]"})

(s/def ::person (s/keys :req [::first-name ::last-name ::email]))

Оценка

pre-post-messages.core> (person-name  try-2)

создаст

"Anna Vissi"

и оценивая

pre-post-messages.core> (person-name  try-1)

создаст

FAIL in () (core.clj:6)

expected: (s/valid? :pre-post-messages.core/person person)

  actual: (not (s/valid? :pre-post-messages.core/person {:first-name "Anna Vissi"}))

AssertionError Assert failed: (t/is (s/valid? :pre-post-messages.core/person person))  pre-post-messages.core/person-name (core.clj:5)