Meaningful error message for Clojure.Spec validation in :pre
我用了最后的日子来深入研究Clojure和ClojureScript中的clojure.spec。
到目前为止,我发现它最有用,它是在依赖某种格式数据的公共函数中的
1 2 3 4 | (defn person-name [person] {:pre [(s/valid? ::person person)] :post [(s/valid? string? %)]} (str (::first-name person)"" (::last-name person))) |
这种方法的问题是,我得到一个
有谁知道如何在
我知道
在较新的Alpha中,现在提供了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | (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} |
我认为这个想法是您使用
在此博客文章的底部有一个很好的例子:http://gigasquidsoftware.com/blog/2016/05/29/one-fish-spec-fish/。快速总结:您可以使用:args和:ret键定义函数的规范,包括输入值和返回值(从而替换前置条件和后置条件),并使用
从该链接派生的最小示例:
1 2 3 4 5 6 | (spec/fdef your-func :args even? :ret string?) (spec/instrument #'your-func) |
这等效于将函数具有整数参数作为前提,将函数返回一个字符串作为后置条件。就像您在寻找一样,除了得到更多有用的错误外。
官方指南中的更多详细信息:https://clojure.org/guides/spec ---参见"规范功能"标题下的内容。
不考虑是否应该使用前置条件和后置条件来验证函数参数,而是通过用
如何获取Clojure:pre和:post报告其失败值?
因此,您的代码可能如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | (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])) |
评估
1 | pre-post-messages.core> (person-name try-2) |
会产生
1 | "Anna Vissi" |
和评估
1 | pre-post-messages.core> (person-name try-1) |
会产生
1 2 3 4 5 6 7 | 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) |
当您不想使用
"
1 2 3 4 5 6 | (defn validate [spec input] (let [explanation (s/explain-str spec input)] (if (= explanation"Success!\ ") true (throw (ex-info explanation {:input input})))) |
可以是这种形式的变体,但是它将运行两次规范:
1 2 3 4 | (defn validate [spec input] (if (s/valid? spec input) true (throw (ex-info (s/explain spec input) {:input input})))) |
用法:
1 2 3 | (defn person-name [person] {:pre [(validate ::person person)]} (str (::first-name person)"" (::last-name person))) |