Как я могу получить Clojure: pre &: post, чтобы сообщить о своем неудачном значении?
(defn string-to-string [s1]
{:pre [(string? s1)]
:post [(string? %)]}
s1)
Мне нравится: pre и: post conditions, они позволяют мне разобраться, когда я поставил "квадратные колышки в круглых отверстиях" быстрее. Возможно, это неправильно, но мне нравится использовать их в качестве своего рода плохой проверки типа mans. Это не философия, но это простой вопрос.
Кажется, что в приведенном выше коде я легко могу определить, что s1
является аргументом функции в условии :pre
. Аналогично, %
в :post
является всегда возвращаемым значением функции.
Я хотел бы напечатать значение s1
или %
, если одно из этих соответствующих условий завершится с ошибкой в AssertionError. Поэтому я получаю что-то вроде
(string-to-string 23)
AssertionError Assert failed: (string? s1)
(pr-str s1) => 23
С AssertionError, содержащим одну строку для каждой переменной, которая была идентифицирована как из списка аргументов функции, и которая была указана в тесте с ошибкой. Мне также хотелось бы что-то подобное, когда возвращаемое значение функции завершится с ошибкой :post
.
Это сделало бы тривиальным быстро определить, как я неправильно использовал функцию при попытке диагностики из AssertionError. Это по крайней мере сообщит мне, если значение nil
или фактическое значение (которое является самой распространенной ошибкой, которую я делаю).
У меня есть некоторые идеи, что это можно сделать с помощью макроса, но мне было интересно, существует ли какой-либо безопасный и глобальный способ в принципе просто переопределить, что (defn
и (fn
и друзья делают так, что :pre
и :post
также печатает значения, которые приводят к сбою теста.
Ответы
Ответ 1
Вы можете перенести свой предикат с помощью макроса is
из clojure.test
(defn string-to-string [s1]
{:pre [(is (string? s1))]
:post [(is (string? %))]}
s1)
Затем вы получите:
(string-to-string 10)
;FAIL in [email protected] (scratch.clj:5)
;expected: (string? s1)
;actual: (not (string? 10))
Ответ 2
@octopusgrabbus вроде намекнул на это, предложив (try ... (catch ...))
, и вы упомянули, что это может быть слишком шумно и по-прежнему завернуто в assert. Простым и менее шумным вариантом этого будет простой синтаксис (or (condition-here) (throw-exception-with-custom-message))
, например:
(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)
Это, по сути, позволяет использовать предварительные и пост-условия с настраиваемыми сообщениями об ошибках - предварительные и пост-условия все еще проверяются, как и обычно, но ваше настраиваемое исключение оценивается (и таким образом бросается) перед AssertionError может случиться.
Ответ 3
Что-то вроде ниже, где clojure spec объясняет проблему? Это вызовет ошибку утверждения, которую вы можете поймать.
(defn string-to-string [s1]
{:pre [ (or (s/valid? ::ur-spec-or-predicate s1)
(s/explain ::ur-spec-or-predicate s1)]}
s1)