Más contenido relacionado La actualidad más candente (20) Similar a Spectacular Future with clojure.spec (20) Spectacular Future with clojure.spec8. リテラル
type example
string "abc"
character a
number 1, 2.0, 3N, 4.5M, 6/7, 8r10
boolean true, false
nil nil
keyword :a, :user/a, ::a, ::x/a
symbol 'a, 'user/a, `a, `x/a
9. type example
list '(1 2 3), '(+ 1 2 3)
vector [1 2 3]
set #{1 2 3}
map {:a 1 :b 2}, #:user{:a 1 :b 2},
#::{:a 1 :b 2}, #::x{:a 1 :b 2}
function (fn [x] (* x x))
13. エラー
Javaのスタックトレースが\(^o^)/……
;; マップの値の型が数値ではなく⽂字列
user> (cuboid-volume {:side-a 1 :side-b "2" :side-c 3})
ClassCastException java.lang.String cannot be cast to java.lang.
Number clojure.lang.Numbers.multiply (Numbers.java:148)
;; マップのキー名でtypo
user> (cuboid-volume {:side-a 1 :side-d 2 :side-c 3})
NullPointerException clojure.lang.Numbers.ops (Numbers.java:10
18)
;; マップに必須のキーがない
user> (cuboid-volume {:side-a 1 :side-c 3})
NullPointerException clojure.lang.Numbers.ops (Numbers.java:10
18)
22. 「⻑さ」のサンプルを⽣成
で 互換なジェネレータを取得
でサンプルを⽣成
user> (s/gen ::length)
#clojure.test.check.generators.Generator{:gen #function[clojure.
test.check.generators/such-that/fn--13745]}
user> (gen/sample (s/gen ::length))
(0.5 -1.0 -1 0.5 3.25 0 -3 0.6875 0.25 0)
s/gen test.check
gen/sample
23. 「⻑さ」に制約を加える
論理演算⼦で制約を追加
は論理積
cf.
user> (s/def ::length (s/and number? pos?))
:user/length
user> (doc ::length)
-------------------------
:user/length
Spec
(and number? pos?)
nil
user> (gen/sample (s/gen ::length))
(0.5 3.0 0.5 1.375 1.6875 3.0 0.625 1.25 0.41015625 0.25)
s/and
s/or
24. 「⻑さ」で辺a, b, cを表現
user> (s/def ::side-a ::length)
:user/side-a
user> (doc ::side-a)
-------------------------
:user/side-a
Spec
(and number? pos?)
nil
user> (s/def ::side-b ::length)
:user/side-b
user> (s/def ::side-c ::length)
:user/side-c
26. 直⽅体に⼀致する値を調べる
user> (s/conform ::cuboid #::{:side-a 1
:side-b 2
:side-c 3})
#:user{:side-a 1, :side-b 2, :side-c 3}
user> (s/conform ::cuboid #::{:side-a 1
:side-b "2"
:side-c 3})
:clojure.spec.alpha/invalid
28. 値 #:user{:side-a 1, :side-d 2, :side-c
3} がspec :user/cuboid の述語 (contains? %
:user/side-b) に不⼀致
user> (s/explain ::cuboid #::{:side-a 1
:side-d 2
:side-c 3})
val: #:user{:side-a 1, :side-d 2, :side-c 3} fails spec: :user/c
uboid predicate: (contains? % :user/side-b)
:clojure.spec.alpha/spec :user/cuboid
:clojure.spec.alpha/value #:user{:side-a 1, :side-d 2, :side-c
3}
nil
29. 値 #:user{:side-a 1, :side-c 3} がspec
:user/cuboid の述語 (contains? %
:user/side-b) に不⼀致
user> (s/explain ::cuboid #::{:side-a 1
:side-c 3})
val: #:user{:side-a 1, :side-c 3} fails spec: :user/cuboid predi
cate: (contains? % :user/side-b)
:clojure.spec.alpha/spec :user/cuboid
:clojure.spec.alpha/value #:user{:side-a 1, :side-c 3}
nil
30. 直⽅体のサンプルを⽣成
複合的なデータのサンプルも⽣成できる
user> (gen/sample (s/gen ::cuboid))
(#:user{:side-a 0.5, :side-b 1.0, :side-c 0.625} #:user{:side-a
0.5, :side-b 1.5, :side-c 2} #:user{:side-a 2.0, :side-b 4, :sid
e-c 1} #:user{:side-a 1.5, :side-b 1.0, :side-c 1} #:user{:side-
a 0.5, :side-b 12, :side-c 1.125} #:user{:side-a 49, :side-b 0.3
59375, :side-c 0.765625} #:user{:side-a 2, :side-b 1, :side-c 1.
25} #:user{:side-a 3.0, :side-b 0.5, :side-c 2.0} #:user{:side-a
1.3125, :side-b 1.0, :side-c 1.0} #:user{:side-a 5.265625, :sid
e-b 1.0625, :side-c 2.73828125})
35. パス [0 :user/side-b] の値 "2" がspec
:user/length の述語 number? に不⼀致 ⇒ 例外
user> (cuboid-volume #::{:side-a 1
:side-b "2"
:side-c 3})
ExceptionInfo Call to #'user/cuboid-volume did not conform to sp
ec:
In: [0 :user/side-b] val: "2" fails spec: :user/length at: [:arg
s :cuboid :user/side-b] predicate: number?
:clojure.spec.alpha/spec #object[clojure.spec.alpha$regex_spec_
impl$reify__1200 0x57e771b6 "clojure.spec.alpha$regex_spec_impl$
reify__1200@57e771b6"]
:clojure.spec.alpha/value (#:user{:side-a 1, :side-b "2", :side
-c 3})
:clojure.spec.alpha/args (#:user{:side-a 1, :side-b "2", :side-
c 3})
:clojure.spec.alpha/failure :instrument
:clojure.spec.test.alpha/caller {:file "form-init41134222269549
81451.clj", :line 188, :var-scope user/eval14130}
clojure.core/ex-info (core.clj:4744)
36. 関数の動作確認
で引数のspecを満たすランダム
な値で動作確認
結果はベクター [引数 戻り値] のシーケンス
user> (s/exercise-fn `cuboid-volume)
([(#:user{:side-a 2.0, :side-b 0.5, :side-c 0.5}) 0.5] [(#:user{
:side-a 0.75, :side-b 1.5, :side-c 0.75}) 0.84375] [(#:user{:sid
e-a 2.0, :side-b 1, :side-c 1.75}) 3.5] [(#:user{:side-a 4, :sid
e-b 4, :side-c 2}) 32] [(#:user{:side-a 2, :side-b 6, :side-c 1.
0}) 12.0] [(#:user{:side-a 1, :side-b 3, :side-c 6}) 18] [(#:use
r{:side-a 20, :side-b 1.0, :side-c 99}) 1980.0] [(#:user{:side-a
12, :side-b 890, :side-c 1.25}) 13350.0] [(#:user{:side-a 4, :s
ide-b 0.99609375, :side-c 2.0}) 7.96875] [(#:user{:side-a 3, :si
de-b 6.0, :side-c 9}) 162.0])
s/exercise-fn
37. ⾃動プロパティベーストテスト
stest/check
user> (stest/check `cuboid-volume)
({:spec #object[clojure.spec.alpha$fspec_impl$reify__1215 0x765acd43 "c
:cause "integer overflow"
:via
[{:type java.lang.ArithmeticException
:message "integer overflow"
:at [clojure.lang.Numbers throwIntOverflow "Numbers.java" 1526]}]
:trace
[[clojure.lang.Numbers throwIntOverflow "Numbers.java" 1526]
[clojure.lang.Numbers multiply "Numbers.java" 1892]
[clojure.lang.Numbers$LongOps multiply "Numbers.java" 472]
[clojure.lang.Numbers multiply "Numbers.java" 148]
[user$cuboid_volume invokeStatic "form-init4113422226954981451.clj" 1
[user$cuboid_volume invoke "form-init4113422226954981451.clj" 155]
[clojure.lang.AFn applyToHelper "AFn.java" 154]
[clojure.lang.AFn applyTo "AFn.java" 144]
[clojure.core$apply invokeStatic "core.clj" 657]
[clojure.core$apply invoke "core.clj" 652]
[clojure.spec.test.alpha$check_call invokeStatic "alpha.clj" 292]
40. デフォルト1000回の試⾏で正常にテストをパス
user> (stest/check `cuboid-volume)
({:spec #object[clojure.spec.alpha$fspec_impl$reify__1215 0x765a
cd43 "clojure.spec.alpha$fspec_impl$reify__1215@765acd43"], :clo
jure.spec.test.check/ret {:result true, :num-tests 1000, :seed 1
505803853835}, :sym user/cuboid-volume})
user> (stest/summarize-results *1)
{:sym user/cuboid-volume}
{:total 1, :check-passed 1}
41. 最終結果
(ns spec-examples.geometry
(:require [clojure.spec.alpha :as s]))
;; specs
(s/def ::length (s/and number? pos?))
(s/def ::side-a ::length)
(s/def ::side-b ::length)
(s/def ::side-c ::length)
(s/def ::cuboid (s/keys :req [::side-a ::side-b ::side-c])
(s/fdef cuboid-volume
:args (s/cat :cuboid ::cuboid)
:ret number?)
;; implementation
(defn cuboid-volume [{::keys [side-a side-b side-c]}]
(*' side-a side-b side-c))