This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-06-20
Channels
- # ai (4)
- # aleph (1)
- # babashka (127)
- # beginners (89)
- # calva (44)
- # cider (22)
- # clerk (74)
- # clj-commons (5)
- # clj-kondo (3)
- # cljs-dev (51)
- # clojure (117)
- # clojure-europe (22)
- # clojure-nl (2)
- # clojure-norway (100)
- # clojure-uk (2)
- # clojurescript (64)
- # data-science (26)
- # datalevin (3)
- # datascript (2)
- # emacs (10)
- # events (5)
- # figwheel-main (12)
- # helix (2)
- # honeysql (15)
- # hoplon (3)
- # jobs-discuss (32)
- # malli (3)
- # polylith (3)
- # re-frame (2)
- # reitit (15)
- # releases (2)
- # sci (14)
- # shadow-cljs (14)
- # specter (2)
- # tools-build (7)
- # xtdb (16)
I am currently learning to write clojure test, would you recommend against writing test like this?
(defn damage [player value] (update player :life - value))
(defn heal [player value] (update player :life + value))
(deftest damage-the-player
(-> {:life 100 :max-life 100}
(damage 10)
(doto (as-> player
(testing "player is damaged"
(is (= (:life player) 90)))))
(heal 200)
(doto (as-> player
(testing "player is healed and will not exceed max life"
(is (= (:life player) 100)))))))
a little context, example above use game terms but I am not actually building a game, just playing around with names. What I am actually testing is some kind of a stateful web server where I would test a sequence of api calls and saves intermediate results via this map passing (I think I read somewhere else that this is called context map)In tests, I lean toward verbose, clear naming, so I would probably give the intermediate values names in let
instead of threading the whole thing (e.g., player
, damaged-player
, healed-player
).
I'd also break that test in two: one for damage cases (what happens when damage is zero, negative, equal to life, larger than life) and one for healing cases (zero, negative, equal to max/life difference, greater than that).
thanks for the input! I see, explicitly naming the different state it would produce sounds better and I would definitely write the tests for each function separately, it’s just that this example I thought of is another test where I want to make sure sequence of actions would still produce what I expected, also kinda become a small documentation of the flow that I want to create (since I am trying out what if I try writing the test first before the code)
Yeah, there's no harm in having a combined test as well as the individual tests, and the combined test could have the threaded workflow to match the intended usage:
(deftest test-damage-heal-workflow
(testing "combined workflow with damage and healing"
(let [test-player (-> {:life 100 :max-life 100} (damage 10) (heal 200))]
(is (= 100 (:life player))))))
Hi all, I am learning Clojure and was reading "Clojure in Action". I am running into an issue with this code
(defn stub-fn [return-value]
(fn [&args]
return-value))
(defmacro stubbing [stub-forms & body]
(let [stub-pairs (partition 2 stub-forms)
returns (map last stub-pairs)
stub-fns (map #(list 'stub-fn %) returns)
real-fns (map first stub-pairs)]
`(binding [~@(interleave real-fns stub-fns)]
~@body)))
(defn calc-x [x1 x2]
(* x1 x2))
(defn calc-y [x1 x2]
(/ x1 x2))
(defn some-client []
(println (calc-x 2 3) (calc-y 10 20)))
(some-client)
(comment (stubbing [calc-x 1
calc-y 2]
(some-client)))
And here is the error that I run into
; --------------------------------------------------------------------------------
; eval (current-form): (some-client)
; (out) 6 1/2
nil
; --------------------------------------------------------------------------------
; eval (current-form): (stubbing [calc-x 1 calc-y 2] (so...
; (err) Execution error (IllegalStateException) at in-action.stubbing/eval7020 (REPL:25).
; (err) Can't dynamically bind non-dynamic var: in-action.stubbing/calc-y
Can someone explain what I am doing wrong. Thanks.Unfortunately, Clojure in Action is an old book and both editions were outdated by the time of their publication (because the publisher wouldn't allow the author extra time to update the book for the then-current version of Clojure).
(Clojure in Action 1st ed was one of the first Clojure books I bought, and I have the 2nd ed as well)
You've stumbled across some code in CiA that is no longer valid: binding
only works with Vars that are declared ^:dynamic
now.
with-redefs
is the way to temporarily redefine functions for use in an expression like this -- but it should only be used in very limited situations, such as testing. See https://clojuredocs.org/clojure.core/with-redefs
user=> (with-redefs [calc-x (constantly 1)
calc-y (constantly 2)]
(some-client))
1 2
nil
user=>
That makes sense, thanks.
(and even in testing you might have problems with with-redefs if you run tests in parallel)
1st ed was written against Clojure 1.3 but published just after 1.4 came out, and 2nd ed was written against 1.5 but published just after 1.6 came out, I think. Lots of changes and additions since then.
@U05AH90263E I'm curious what editor/REPL setup you're using that produces output like that?
I am using nvim and I followed https://github.com/Olical/conjure/wiki/Quick-start:-Clojure blog for my setup.
Oh, cool! That's one of the few Clojure editor setups I'm not familiar with, so I didn't recognize the REPL output format.
Notably, @U05AH90263E, this question does not come up very often. It seems to me that "stubbing" did not get popular. It's just too weird to expect the consumers of a function to know what to stub and when. Instead, folks write higher-order functions, which take as parameters the functions they will need to call.
@U0HG4EHMH Thanks for the reply, if you can point me to some resource or documentation that I can read that would help.
Higher-order functions is on pg 308 of my copy of Clojure in Action. But the technique is pervasive. Ring (around page 225-230) also uses it.
Hi all, for some reason when I try to import java.awt, I get a ClassNotFoundException error, here's the bit of code that's relevant (this is from Programming Clojure, I'm trying to implement a snake game):
(ns snake-try.core
(:import (java.awt Color Dimension)
(javax.swing JPanel JFrame Timer JOptionPane)
(java.awt.event ActionListener KeyListener))
(:require [clojure.set :refer :all]))
I use Clojure on Emacs and Arch Linux with Java 17 set as the default option. Any idea what the problem is?Thank you for answering! What does that mean exactly?
I dunno, what is the actually exception you are getting, and what are you doing when you get it?
I'm using CIDER on Emacs, running the last S-expression which is trying to import java.awt, this is the error I get:
2. Unhandled clojure.lang.Compiler$CompilerException
Error compiling src/snake_try/core.clj at (2:12)
#:clojure.error{:phase :compile-syntax-check,
:line 2,
:column 12,
:source
"/home/mayumin/Documents/Programming/Clojure Development/snake-try/src/snake_try/core.clj"}
Compiler.java: 6825 clojure.lang.Compiler/analyze
Compiler.java: 6762 clojure.lang.Compiler/analyze
Compiler.java: 3832 clojure.lang.Compiler$InvokeExpr/parse
Compiler.java: 7126 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6806 clojure.lang.Compiler/analyze
Compiler.java: 6762 clojure.lang.Compiler/analyze
Compiler.java: 3900 clojure.lang.Compiler$InvokeExpr/parse
Compiler.java: 7126 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6806 clojure.lang.Compiler/analyze
Compiler.java: 6762 clojure.lang.Compiler/analyze
Compiler.java: 6137 clojure.lang.Compiler$BodyExpr$Parser/parse
Compiler.java: 5479 clojure.lang.Compiler$FnMethod/parse
Compiler.java: 4041 clojure.lang.Compiler$FnExpr/parse
Compiler.java: 7122 clojure.lang.Compiler/analyzeSeq
Compiler.java: 6806 clojure.lang.Compiler/analyze
Compiler.java: 7191 clojure.lang.Compiler/eval
Compiler.java: 7149 clojure.lang.Compiler/eval
core.clj: 3215 clojure.core/eval
core.clj: 3211 clojure.core/eval
interruptible_eval.clj: 87 nrepl.middleware.interruptible-eval/evaluate/fn/fn
AFn.java: 152 clojure.lang.AFn/applyToHelper
AFn.java: 144 clojure.lang.AFn/applyTo
core.clj: 667 clojure.core/apply
core.clj: 1990 clojure.core/with-bindings*
core.clj: 1990 clojure.core/with-bindings*
RestFn.java: 425 clojure.lang.RestFn/invoke
interruptible_eval.clj: 87 nrepl.middleware.interruptible-eval/evaluate/fn
main.clj: 437 clojure.main/repl/read-eval-print/fn
main.clj: 437 clojure.main/repl/read-eval-print
main.clj: 458 clojure.main/repl/fn
main.clj: 458 clojure.main/repl
main.clj: 368 clojure.main/repl
RestFn.java: 1523 clojure.lang.RestFn/invoke
interruptible_eval.clj: 84 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 56 nrepl.middleware.interruptible-eval/evaluate
interruptible_eval.clj: 152 nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
AFn.java: 22 clojure.lang.AFn/run
session.clj: 218 nrepl.middleware.session/session-exec/main-loop/fn
session.clj: 217 nrepl.middleware.session/session-exec/main-loop
AFn.java: 22 clojure.lang.AFn/run
Thread.java: 833 java.lang.Thread/run
1. Caused by java.lang.ClassNotFoundException
java.awt
After checking which packages are installed on my system, I have both a full and a headless build of JRE17 which are installed... which is weird.
Even running import in a REPL without any code gives me that same error though.
java.awt isn't a class, which is why you are getting an error about it not being found
Ah, thank you!
This is weird because it gives me the same issue when I run the code bundled with the book Programming Clojure, which should be correct...
user=> (ns foo (:import java.awt))
Execution error (ClassNotFoundException) at java.net.URLClassLoader/findClass (URLClassLoader.java:445).
java.awt
foo=>
for exampleAh, I see.
So how should I import java.awt in this namespace?
So should I use (require ...) or something else?
Trying to import the Class and Dimension classes from java.awt, this is from a snake namespace from the book Programming Clojure.
You can see it at the beginning of the thread, if I understand your question correctly?
Yes, I copy pasted the code from Emacs.
user=> (ns snake-try.core
(:import (java.awt Color Dimension)
(javax.swing JPanel JFrame Timer JOptionPane)
(java.awt.event ActionListener KeyListener))
(:require [clojure.set :refer :all]))
nil
snake-try.core=>
(ns snake-try.core (:import (java.awt Color Dimension) (javax.swing JPanel JFrame Timer JOptionPane) (java.awt.event ActionListener KeyListener)) (:require [clojure.set :refer :all])) Here it is again.
Eh...??? Really?
Running it in Emacs gives me the error I mentioned before though...
Is it my system then?
no, you need to look at your setup and make sure the code you expect is running and only that code
Lemme boot up a REPL and try it without anything else.
Okay, I will check my code and see what's wrong, I assume it has to be some error there because I don't see what else might be wrong. I will report back once I find it.
that is the error you would get if you tried to treat the symbol java.awt with normal evaluation rules, instead of the ns macro's
user=> java.awt
Syntax error (ClassNotFoundException) compiling at (REPL:0:0).
java.awt
user=>
user=> (:import (java.awt Color Dimension))
Syntax error (ClassNotFoundException) compiling at (REPL:1:10).
java.awt
user=>
Here is the GUI part of the application, making use of Java:
i would start with adding a (println "Hello World") to the very top of your source file (above even the ns form) and verify that prints before you get the error, and that will verify the source file you are looking at is what you are actually loading
Got it!
Okay: Hello World! does work, and now it's giving me a different error:
Execution error (IllegalArgumentException) at snake-try.core/game (core.clj:190). No matching method setFocusable found taking 1 args for class java.lang.Class
you most likely want to set an instance of a JPanel as focusable. Here JPanel
is just the class java.awt.JPanel
I see... Sorry I'm not sure I understand what that means, this is the code from the book and I don't fully understand it. 😞
Does that mean I should create an actual instance of it and set that as focusable? If so, how do I do this?
Programming Clojure, 3rd edition
This is the end of chapter 6, state and concurrency.
The source code can be found online, and it is the same as what I wrote which is what I don't understand, surely they'd check their code is working first..?
Wait, did I mess up?
Thanks, let me check
Thanks!! It is working now.
Okay, so I was asking it to use it on the class JPanel, instead of using it on the panel object, correct?
i think your initial issue was unsaved buffers. I suspect what code you saw was not the code that was actually in the files. @U0NCTKEV8’s suggestion to add printlines ensured that you saved all of the files
Ahhh, I see! Thank you.
This solves my issue, thank you very much!
correct. There’s a big difference between an instance of a set #{}
and the class clojure.lang.PersistentHashSet
Thank you, this clarified everything! 🙂
I have a question about the snake game as well, should I post it here or make a new thread?
Probably best to make a new thread, you'll get more visibility
Yup, made a new thread