This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-04-18
Channels
- # announcements (1)
- # babashka (39)
- # babashka-sci-dev (59)
- # beginners (60)
- # calva (14)
- # circleci (1)
- # clj-kondo (16)
- # clj-on-windows (1)
- # clojure (95)
- # clojure-europe (5)
- # clojure-norway (2)
- # clojurescript (34)
- # conjure (2)
- # core-async (55)
- # datomic (4)
- # emacs (54)
- # holy-lambda (5)
- # hyperfiddle (2)
- # interop (4)
- # lsp (8)
- # malli (3)
- # nrepl (4)
- # off-topic (34)
- # polylith (5)
- # reitit (3)
- # releases (2)
- # shadow-cljs (85)
- # specter (2)
- # testing (8)
- # tools-deps (12)
#orgmode (with live example featuring Clojure in org-babel) https://www.evalapply.org/posts/why-and-how-i-use-org-mode/
> I have never used org for managing TODOs, calendaring, or any “productivity” use cases (time tracking, GTD etc.), which it has become popular for. Nor, as is also popular these days, is it my “second brain”. (Or maybe it is because my ~/org directory is a total disaster zone. I just disorganise and use Emacs to search through the files.). This made me laugh out loud 😄 ❤️
> I discovered it about a decade ago (circa 2013), right after I changed careers to “tech”, because all the gentlenerds at the little company used Emacs. Told ya. Not normal. (Although, surprisingly, lots of normies use it too. Hi friends!). What did you do before tech?
> Yes m’lorx, this way please typo - "m'lord"? (though m'lorx undoubtably sounds cool)
@nicola The TL;DR is that test tooling doesn't generally expect tests to be in source files so default test runners don't always find such tests. The Expectations library docs talks about that in more detail: https://cljdoc.org/d/com.github.seancorfield/expectations/2.0.160/doc/getting-started#test-placement
Would be an interesting exercise to count which runners do accept src and which do not.
I'd healthily question whether most
and everyone
are accurate wording in that cljdoc :)
And an even more useful exercise would be to report flaws in runners and get them fixed - no real reason to get stuck in suboptimality
@U45T93RA6 that doc explains how to get the test runners to run tests in src - the point I'm making is that it's not the default behavior and it's not what any of the books or tutorials teach. I think co-located tests are fine and clojure.test
inherently supports them (`with-test`) - modulo the caveats.
When I feel like a test belongs with the function in question (e.g. when it would also serve as a part of the documentation) and when it can be expressed as a sequence of arg vectors, I just put that sequence somewhere in the functions metadata and create a separate test that simply runs that function in the right context with all those arguments from the sequence, one by one.
Can you post an example please
Sure, something like this:
;; In `/src`
(ns foo.bar)
(defn do-stuff
"Does some stuff - usually adds two numbers together."
{:test-data {[1 1] 2
[1 2] 3
[-1 1] 0}}
[x y]
(+ x y))
;; In `/test`
(ns foo.bar-test
(:require [clojure.test :refer :all]))
(deftest test-data-in-bar []
(doseq [[sym var] (ns-publics 'foo.bar)]
(when-some [td (:test-data (meta var))]
(doseq [[args expected] td]
(let [result (apply @var args)]
(is (= result expected) (pr-str (list sym args))))))))
In golang they like to do something similar (except they don't define it on the fn (but you could (although the compiler would complain about unused var))): https://yourbasic.org/golang/table-driven-unit-test/ (also wow, getting 8 upvotes is quite rare here 😄)
I'm surprised with that myself. :D My code above is just a lazy man's https://github.com/Kobold/clj-doc-test (and also happens to be more flexible because you're in full control and the implementation is just a few lines).
This alternative means cider-test-run-test (run test at <cursor>
) or any equivalent feature won't work.
also doseq
and when-some
are prone to false negatives here - one doesn't want tests to misteriously stop running
If you'd have test boilerplate for each method then it would still work. Maybe it could be done via a macro. But in the case of a test-executing macro it wouldn't produce a testfile to run. Again, coming from golang world it could be done via a code generator... Where you could generate your test boilerplate file for this method when it has this metadata.
@U45T93RA6 Regarding the cider point - I think I know what you mean and I realized that I used defn
instead of deftest
- that's corrected now.
Regarding doseq
and when-some
- I don't get it. Where would false negatives come from?
I meant something else regarding cider
with the original pattern you can cider-test-run-test over a defn ^{:test ...
, it's super handy as you remain focused in the sources
> Where would false negatives come from?
If you rename tests, or have typos in the :test-data
name, etc the deftest would pass, but running zero assertions
Ah, well, you can easily define your own function that just runs over :test-data
and put it under :test
. That will actually solve your second point as well - that :test
function can assert that there's some test data.
But I myself wouldn't consider a possible typo in :test-data
to be a good argument against using something like :test-data
. You can make typos anywhere that introduce false negatives, there's no generic guard against that.
@U45T93RA6 If you want you could have a build step that validates that all your metadata of the functions has only specified keywords as keys. This way you'd know that you messed up.
@U45T93RA6 Actually you gave me an idea, so here's an improvement that should tick all the boxes. :D
Two downsides though:
• Tools that check for an explicit deftest
in the source code (e.g. Cursive) won't see a test there
• You'll have to include your sources in the list of resources that has to be checked by a test runner
(defn with-test-data [& data]
(assert (even? (count data)))
(fn []
(let [f-under-test (peek clojure.test/*testing-vars*)]
(doseq [[args expected] (partition 2 data)]
(let [result (apply f-under-test args)]
(is (= expected result) (pr-str (concat (list (.-sym f-under-test)) args))))))))
(defn do-stuff
{:test (with-test-data [1 1] 2
[1 2] 3
[-1 1] 0
;; Just to see how a failure looks.
[42 0] 7)}
[x y]
(+ x y))
The normal test meta takes too much real estate in the source file, that's why I upvoted this approach, I like the minimalism.
@U2FRKM4TW It could also be done this way too (not sure if :pre
can go before args).
(defn do-stuff
(merge ;; This one can be something else
{:pre [1 2 3]}
(with-test [1 1] 2
[1 2] 3
[-1 1] 0
;; Just to see how a failure looks.
[42 0] 7))
[x y]
(+ x y))