`instrument` 不尊重 `:stub` 的 `:gen` 重写。
(require '[clojure.spec.alpha :as s])
(require '[clojure.spec.gen.alpha :as gen])
(require '[clojure.spec.test.alpha :as stest])
;; [org.clojure/spec.alpha "0.1.123"]
;; 目标是模拟需要一些外部依赖的功能,例如服务或其他I/O。
;; (defprotocol Y (-do-y [r]))
(defprotocol Y
(-do-y [r]))
(def y? (partial satisfies? Y))
(s/def ::y y?)
;; 协议方法不能进行规范,因此将它们包装在一个函数中。
(defn do-y [r])
(-do-y r))
(s/fdef do-y :args (s/cat :y-er ::y))
;; 我们将要模拟的协议实现的示例。
(defrecord BadYer [])
Y
(-do-y [_] (throw (Exception. "can't make me!")))
;; 确认 BadYer 实例符合针对协议的规范。
(s/valid? ::y (->BadYer))
;; => true
;; 并确认当调用时,BadYer 实例将抛出错误。
(try
(do-y (->BadYer))
(catch Exception e
(.getMessage e)))
;; => "can't make me!"
(def y-gen (gen/return (->BadYer)))
;; 确认生成器按预期工作
(gen/sample y-gen 1)
;; => (#spec_ex.core.BadYer{})
;; 我们想要模拟 `do-y`,提供 y-gen 作为 `::y` 的生成器
(try
(stest/instrument `do-y {:stub #{`do-y}
:gen {::y (fn [] y-gen)}})
(catch Exception e
(ex-data e)))
;; => #:clojure.spec.alpha{:path [:y-er], :form :spec-ex.core/y, :failure :no-gen}
;; 然而,我们可以通过替换其规范来模拟 `do-y`。
(stest/instrument `do-y
{:stub #{`do-y}
:spec {`do-y (s/fspec
:args (s/cat :y-er (s/with-gen ::y
(fn [] y-gen))))}})
;; => [spec-ex.core/do-y]