Following the docstring in the source of clojure.test, I issue the following in the repl:
(use 'clojure.test)
(is (thrown? ArithmeticException (/ 1 0)))
And other variations like substituting ArithmeticException with java.lang.ArithmeticException. Yet the exception is never captured by clojure.test but rather bubbles up all the way to the top:
> #error { :cause "Divide by zero" :via [{:type java.lang.ArithmeticException :message "Divide by zero" :at
> [clojure.lang.Numbers divide "Numbers.java" 158]}] :trace
> [[clojure.lang.Numbers divide "Numbers.java" 158]
> [clojure.lang.Numbers divide "Numbers.java" 3808] [api.core$eval1305
> invokeStatic "form-init8750388124499546857.clj" 1]
> [api.core$eval1305 invoke "form-init8750388124499546857.clj" 1]
> [clojure.lang.Compiler eval "Compiler.java" 6927]
> [clojure.lang.Compiler eval "Compiler.java" 6890] [clojure.core$eval
> invokeStatic "core.clj" 3105] [clojure.core$eval invoke "core.clj"
> 3101] [clojure.main$repl$read_eval_print__7408$fn__7411 invoke
> "main.clj" 240] [clojure.main$repl$read_eval_print__7408 invoke
> "main.clj" 240] [clojure.main$repl$fn__7417 invoke "main.clj" 258]
> [clojure.main$repl invokeStatic "main.clj" 258] [clojure.main$repl
> doInvoke "main.clj" 174] [clojure.lang.RestFn invoke "RestFn.java"
> 1523]
> [clojure.tools.nrepl.middleware.interruptible_eval$evaluate$fn__667
> invoke "interruptible_eval.clj" 87] [clojure.lang.AFn applyToHelper
> "AFn.java" 152] [clojure.lang.AFn applyTo "AFn.java" 144]
> [clojure.core$apply invokeStatic "core.clj" 646]
> [clojure.core$with_bindings_STAR_ invokeStatic "core.clj" 1881]
> [clojure.core$with_bindings_STAR_ doInvoke "core.clj" 1881]
> [clojure.lang.RestFn invoke "RestFn.java" 425]
> [clojure.tools.nrepl.middleware.interruptible_eval$evaluate
> invokeStatic "interruptible_eval.clj" 85]
> [clojure.tools.nrepl.middleware.interruptible_eval$evaluate invoke
> "interruptible_eval.clj" 55]
> [clojure.tools.nrepl.middleware.interruptible_eval$interruptible_eval$fn__712$fn__715
> invoke "interruptible_eval.clj" 222]
> [clojure.tools.nrepl.middleware.interruptible_eval$run_next$fn__707
> invoke "interruptible_eval.clj" 190] [clojure.lang.AFn run
> "AFn.java" 22] [java.util.concurrent.ThreadPoolExecutor runWorker
> "ThreadPoolExecutor.java" 1142]
> [java.util.concurrent.ThreadPoolExecutor$Worker run
> "ThreadPoolExecutor.java" 617] [java.lang.Thread run "Thread.java"
> 745]]}
What might it be?
Other stuff from clojure.test does work in the repl as expected.
Version information:
Leiningen 2.7.1 on Java 1.8.0_101 Java HotSpot(TM) 64-Bit Server VM
> nREPL server started on port 44025 on host 127.0.0.1 -
> nrepl://127.0.0.1:44025 REPL-y 0.3.7, nREPL 0.2.12 Clojure 1.8.0 Java
> HotSpot(TM) 64-Bit Server VM 1.8.0_101-b13
use it/wrap it in (clojure.test/deftest...)
assuming:
(require '[clojure.test :as t])
pass example
(t/deftest a
(t/testing "a test"
(t/is (thrown? ArithmeticException (/ 1 0)))))
(a)
nil
fail example
(t/deftest a
(t/testing "a test"
(t/is (thrown? NullPointerException (/ 1 0)))))
(a)
; ERROR in (a) (Numbers.java:158)
; a test
; expected: (thrown? NullPointerException (/ 1 0))
; actual: java.lang.ArithmeticException: Divide by zero
you can also use t/run-tests to get a summary/stats
alternatively it seems you can use t/test-var directly like so
(t/test-var (t/is (thrown? ArithmeticException (/ 1 0))))
nil
You may also be interested in the throws? macro
from the Tupelo library. It is more versatile and
easier to use than the thrown? clause from clojure.test. Note that thrown? in clojure.test is not a normal
Clojure function or macro, but is a custom expression recognized only within the is macro).
The tupelo.test/throws? macro is a normal Clojure macro that can be used anywhere.
throws? is meant to be used alone, and should not be wrapped within an is statement.
Note that throws? always returns either true or false, which can be used in further processing.
> lein repl
user=> (use 'tupelo.test)
user=> (throws? (/ 1 0))
true ; <= return value
user=> (throws? Exception (/ 1 0))
true ; <= return value
user=> (throws? ArithmeticException (/ 1 0))
true ; <= return value
If the expression does not thrown an exception, or thows a different type of exception, then
throws? will register a failed test via (clojure.test/is false), and will also return false:
user=> (throws? NullPointerException (/ 1 0))
FAIL in () (form-init2868942318552383212.clj:1)
expected: (try (/ 1 0) false (catch NullPointerException t1__13775__auto__ true) (catch java.lang.Throwable t2__13776__auto__ false))
actual: false
false ; <= return value
clj.core=> (throws? ArithmeticException (+ 1 0))
FAIL in () (form-init4299343763551622058.clj:1)
expected: (try (+ 1 0) false (catch ArithmeticException t1__13761__auto__ true) (catch java.lang.Throwable t2__13762__auto__ false))
actual: false
false ; <= return value
clj.core=> (throws? (+ 1 0))
FAIL in () (form-init4299343763551622058.clj:1)
expected: (try (+ 1 0) false (catch java.lang.Throwable t3__13763__auto__ true))
actual: false
false ; <= return value
A typical usage of throws? is illustrated by tupelo.core's own unit tests:
(ns test.tupelo.core
(:use clojure.test tupelo.test )
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(deftest t-grab
(let [map1 {:a 1 :b 2}]
(is= 1 (grab :a map1))
(is= 2 (grab :b map1))
(throws? (grab :c map1)) ))
Related
The following example in clojure calls the nullary and binary cases of + in various ways:
(println 101 (+)) ; fine
(println 102 (+ (+) 4)) ; fine
(println 103 (reduce + (+) (range 4))) ; fine
(println 104 (reduce + (range 4))) ; fine
I tried replacing + with mean-reducer as described in this blog post (warning: no https).
I changed the mean-reducer function to explicitly expose its identity element {:sum 0 :count 0} when invoked with no arguments.
This works fine for the simple cases leading up to (reduce mean-reducer (range 4)), but falls over for (reduce mean-reducer (range 4)) itself.
(defn mean-reducer
([] {:sum 0 :count 0})
([memo x]
{
:sum (+ x (memo :sum))
:count (inc (memo :count))
}))
(println 201 (mean-reducer)) ; fine
(println 202 (mean-reducer (mean-reducer) 4)) ; fine
(println 203 (reduce mean-reducer (mean-reducer) (range 4))) ; fine
;; (println 204 (reduce mean-reducer (range 4))) ; bad
runs and produces this with the last line commented.
% clojure mean_reducer.clj
201 {:sum 0, :count 0}
202 {:sum 4, :count 1}
203 {:sum 6, :count 4}
The error message and stack trace associated with the failed call to (reduce mean-reducer (range 4)) looks like this:
(~/clojure/mean_reducer.clj:12:62).
at clojure.lang.Compiler.load(Compiler.java:7647)
at clojure.lang.Compiler.loadFile(Compiler.java:7573)
at clojure.main$load_script.invokeStatic(main.clj:452)
at clojure.main$script_opt.invokeStatic(main.clj:512)
at clojure.main$script_opt.invoke(main.clj:507)
at clojure.main$main.invokeStatic(main.clj:598)
at clojure.main$main.doInvoke(main.clj:561)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.lang.Var.applyTo(Var.java:705)
at clojure.main.main(main.java:37)
Caused by: java.lang.ClassCastException: class java.lang.Long cannot be cast to class clojure.lang.IFn (java.lang.Long is in module java.base of loader 'bootstrap'; clojure.lang.IFn is in unnamed module of loader 'app')
at user$mean_reducer.invokeStatic(mean_reducer.clj:5)
at user$mean_reducer.invoke(mean_reducer.clj:1)
at clojure.lang.LongRange.reduce(LongRange.java:222)
at clojure.core$reduce.invokeStatic(core.clj:6823)
at clojure.core$reduce.invoke(core.clj:6810)
at user$eval143.invokeStatic(mean_reducer.clj:13)
at user$eval143.invoke(mean_reducer.clj:13)
at clojure.lang.Compiler.eval(Compiler.java:7176)
at clojure.lang.Compiler.load(Compiler.java:7635)
... 9 more
I think this means that somehow the lines are getting crossed and an element of (range 4) is being bound to memo, but I'm not sure why this would happen given that the case with an explicit initial element succeeds.
When you pass no initial value to reduce the 2-arity version of mean-reducer is invoked with the first two elements of the range i.e. (mean-reducer 0 1). From reduce docs:
If val is not supplied, returns the result of applying f to the first 2 items in coll, then applying f to that result and the 3rd item, etc.
You'll need to supply an initial value to reduce if you want to use mean-reducer (and its 0-arity won't be used). reduce has two different "contracts" for its 2-arity reducing function depending on whether an initial value is specified.
clojure.core.reducers
clojure.core.reducers/reduce has exactly the behavior you want when no initial value is supplied:
When init is not provided, (f) is used.
(r/reduce mean-reducer (range 4))
=> {:sum 6, :count 4}
If I define the following function
(defn catcher [x] (try
(load-string x)
(catch Exception e
(prn "caught"))) )
(catcher "(+ 2 \"2\")") => "caught"
but (catcher "(keys [1 2])")=> ClassCastException java.lang.Long cannot be cast to java.util.Map$Entry
Normally both of these inputs throw ClassCastException, so why is only the first one being caught?
It seems that the exception is happening when trying to print the result of (keys [1 2]).
(type (catcher "(keys [1 2])"))
=> clojure.lang.APersistentMap$KeySeq
Here you can see the expression is actually creating/returning a KeySeq without throwing/catching an exception. It's only when that KeySeq is printed that the exception is thrown:
java.lang.ClassCastException: java.lang.Long cannot be cast to java.util.Map$Entry
at clojure.lang.APersistentMap$KeySeq.first(APersistentMap.java:168)
at clojure.lang.RT.first(RT.java:685)
at clojure.core$first__5107.invokeStatic(core.clj:55)
at clojure.core$print_sequential.invokeStatic(core_print.clj:64)
at clojure.core$fn__7021.invokeStatic(core_print.clj:174)
at clojure.core$fn__7021.invoke(core_print.clj:174)
at clojure.lang.MultiFn.invoke(MultiFn.java:233)
at clojure.tools.nrepl.middleware.pr_values$pr_values$fn$reify__784.send(pr_values.clj:35)
Notice in the stack trace this exception is originating from KeySeq.first method (which isn't being called until after your function has created and returned its value) when the REPL tries to realize the sequence for printing.
Please note that [1 2], when you type it, is a clojure vector literal, not a Clojure MapEntry. See these results:
(ns tst.demo.core
(:use tupelo.test)
(:require
[tupelo.core :as t] ) )
(t/refer-tupelo)
(dotest
(newline)
(let [my-map {:a 1 :b 2}
map-entries (vec my-map)
map-entry-1 (first map-entries)
map-keys (keys my-map)
entry-1-key (key map-entry-1)
]
(is= map-entries [[:a 1] [:b 2]] )
(is= map-entry-1 [:a 1] )
(is= map-keys [:a :b])
(is= entry-1-key :a)
(spyxx my-map)
(spyxx map-entries)
(spyxx map-entry-1)
(spyxx map-keys)
(spyxx entry-1-key)
))
with results:
Testing tst.demo.core
my-map => <#clojure.lang.PersistentArrayMap {:a 1, :b 2}>
map-entries => <#clojure.lang.PersistentVector [[:a 1] [:b 2]]>
map-entry-1 => <#clojure.lang.MapEntry [:a 1]>
map-keys => <#clojure.lang.APersistentMap$KeySeq (:a :b)>
entry-1-key => <#clojure.lang.Keyword :a>
Ran 2 tests containing 4 assertions.
0 failures, 0 errors.
The problem is that a MapEntry prints the same as a vector:
`[1 2]`
However, they are different types. When you do (load-string "[1 2]") it is returning the vector, not a map, so you cannot call the keys function.
Part #2
In your original question, you ask about
(catcher "(keys [1 2])") => Exception
I suspect that load-string is returning a lazy result, and this is not realized until after your code exits the try-catch block, and this is why the exception is not caught.
In trying to use spec library, I'm getting errors in attempting to use exercise-fn. I've reduced this to the posted example at the main guide page with no change.
relevant code:
(ns spec1
(:require [clojure.spec.alpha :as s]))
;;this and the fdef are literal copies from the example page
(defn adder [x] #(+ x %))
(s/fdef adder
:args (s/cat :x number?)
:ret (s/fspec :args (s/cat :y number?)
:ret number?)
:fn #(= (-> % :args :x) ((:ret %) 0)))
Now, typing the following
(s/exercise-fn adder)
gives the error:
Exception No :args spec found, can't generate clojure.spec.alpha/exercise-fn (alpha.clj:1833)
Dependencies/versions used, [org.clojure/clojure "1.9.0-beta3"]
[org.clojure/tools.logging "0.4.0"]
[org.clojure/test.check "0.9.0"]
Anyone have any ideas as to why this is breaking? Thanks.
You need to backquote the function name, which will add the namespace prefix:
(s/exercise-fn `adder)
For example, in my test code:
(s/fdef ranged-rand
:args (s/and
(s/cat :start int? :end int?)
#(< (:start %) (:end %) 1e9)) ; need add 1e9 limit to avoid integer overflow
:ret int?
:fn (s/and #(>= (:ret %) (-> % :args :start))
#(< (:ret %) (-> % :args :end))))
(dotest
(when true
(stest/instrument `ranged-rand)
(is (thrown? Exception (ranged-rand 8 5))))
(spyx (s/exercise-fn `ranged-rand)))
Which results in:
(s/exercise-fn (quote tst.tupelo.x.spec/ranged-rand))
=> ([(-2 0) -1] [(-4 1) -1] [(-2 0) -2] [(-1 0) -1] [(-14 6) -4]
[(-36 51) 45] [(-28 -3) -7] [(0 28) 27] [(-228 -53) -130] [(-2 0) -1])
Note that the namespace-qualified function name tst.tupelo.x.spec/ranged-rand is used.
(try
(/ 1 0)
(catch Exception e
(prn e)))
prints in REPL as
#error {
:cause Divide by zero
:via
[{:type java.lang.ArithmeticException
:message Divide by zero
:at [clojure.lang.Numbers divide Numbers.java 158]}]
:trace
[[clojure.lang.Numbers divide Numbers.java 158]
......................
[java.lang.Thread run Thread.java 745]]}
How to get this complete and comprehensible message with str?
(try
(/ 1 0)
(catch Exception e
(str e)))
return only
=> "java.lang.ArithmeticException: Divide by zero"
Solution is very simple
(pr-str e)
I'm trying to write my first simple graphics app using Quil. Right now, I'm just trying to draw a dot that moves diagonally across the screen.
(ns quil-test.quil-first
(:require [quil.core :as q])
(:gen-class))
(defn setup-state []
(q/frame-rate 60)
{:x 0})
(defn update-state [s]
(assoc s :x (q/frame-count)))
(defn draw-state [state]
(let [x (:x state)]
(q/stroke-weight 100)
(q/point x x)))
(q/defsketch quil-first
:size [500 500]
:setup setup-state
:update update-state
:draw draw-state)
The problem is, running this doesn't show a dot, and the following error is repeatedly printed to the console:
Exception in :draw function: #error {
:cause Wrong number of args (0) passed to: quil-first/draw-state
:via
[{:type clojure.lang.ArityException
:message Wrong number of args (0) passed to: quil-first/draw-state
:at [clojure.lang.AFn throwArity AFn.java 429]}]
:trace
[[clojure.lang.AFn throwArity AFn.java 429]
[clojure.lang.AFn invoke AFn.java 28]
[clojure.lang.Var invoke Var.java 375]
[quil.middlewares.safe_fns$wrap_fn$fn__114 invoke safe_fns.clj 9]
[quil.middlewares.bind_output$bind_output$iter__148__152$fn__153$fn__164 invoke bind_output.clj 21]
[quil.applet$_draw invoke applet.clj 220]
[quil.Applet draw nil -1]
[processing.core.PApplet handleDraw PApplet.java 2402]
[quil.Applet handleDraw nil -1]
[processing.awt.PSurfaceAWT$12 callDraw PSurfaceAWT.java 1527]
[processing.core.PSurfaceNone$AnimationThread run PSurfaceNone.java 316]]}
stacktrace: clojure.lang.ArityException: Wrong number of args (0) passed to: quil-first/draw-state
at clojure.lang.AFn.throwArity (AFn.java:429)
clojure.lang.AFn.invoke (AFn.java:28)
clojure.lang.Var.invoke (Var.java:375)
quil.middlewares.safe_fns$wrap_fn$fn__114.invoke (safe_fns.clj:9)
quil.middlewares.bind_output$bind_output$iter__148__152$fn__153$fn__164.invoke (bind_output.clj:21)
quil.applet$_draw.invoke (applet.clj:220)
quil.Applet.draw (:-1)
processing.core.PApplet.handleDraw (PApplet.java:2402)
quil.Applet.handleDraw (:-1)
processing.awt.PSurfaceAWT$12.callDraw (PSurfaceAWT.java:1527)
processing.core.PSurfaceNone$AnimationThread.run (PSurfaceNone.java:316)
It's saying that my draw-state function takes 0 arguments, when it should take 1. draw-state clearly takes 1 argument though.
I don't understand why draw-state is somehow being "converted" into a 0-arity function.
The problem is I didn't activate "fun(ctional) mode", so the draw function expected to take 0 arguments, since by default state is managed globally.
This works:
(ns quil-test.quil-first
(:require [helpers.general-helpers :as g]
[quil.core :as q]
[quil.middleware :as m])
(:gen-class))
(defn setup-state []
(q/frame-rate 60)
{:x 0})
(defn update-state [s]
(g/update-with s :x (fn [_] (q/frame-count))))
(defn draw-state [state]
(let [x (:x state)]
(q/stroke-weight 100)
(q/point x x)))
(q/defsketch quil-first
:size [500 500]
:setup setup-state
:update update-state
:draw draw-state
:middleware [m/fun-mode]) ; Activate functional mode