Fork me on GitHub
#beginners
<
2024-03-06
>
Joseph Graham05:03:06

I wrote some tests like this to apply a different test to each key of a map:

(let [mymap {:key1 5 :key2 10 :key3 "foo"}]
        (clojure.test/are [f k] (f (get mymap k))
          #(= 5 %) :key1
          #(< 9 %) :key2
          string? :key3))
However ChatGPT thinks it's bad practice: > This usage is quite clever because it allows you to compactly specify a series of tests that apply different predicates to different map entries. However, it's worth noting that this is a somewhat unconventional use of clojure.test/are, which is generally used to apply the same test logic to different inputs. While this approach works and is concise, it might be less clear to others maintaining your code, especially if they expect clojure.test/are to be used in the standard way for repetitive tests with the same logic. > Always consider the readability and maintainability of your code, especially in team environments or when others might be reading your code later. Is it OK or should I listen to ChatGPT?

Joseph Graham05:03:46

hm maybe I should try using spec or something

seancorfield06:03:44

I would say "you shouldn't listen to ChatGPT" but I also don't think this is a readable test...

2
seancorfield06:03:20

It looks like a contrived example. What are you really trying to test? Maybe Spec is what you want (but it's hard to tell from your question).

seancorfield06:03:23

My preferred testing library is Expectations (the clojure.test compatible version) so I would write:

user=> (require '[expectations.clojure.test :refer [expect more-of]])
nil
user=> (expect (more-of {:keys [key1 key2 key3]}
                        5 key1
                        #(< 9 %) key2
                        string? key3)
               {:key1 5 :key2 10 :key3 "foo"})
true
user=>

seancorfield06:03:59

And if you have a Spec, you could just write (expect ::my-spec {:key1 5 :key2 10 :key3 "foo"})

Joseph Graham06:03:14

I'm trying to test the response map of a ring handler

seancorfield06:03:52

We use a combination of Spec and Expectations for that at work.

Joseph Graham06:03:38

OK I'll look into expectations

seancorfield06:03:01

(defexpect good-fetch

  (expect ::good-response
          (api-get "members/self/activity"
                    (auth-token "derekpope" "pope"))))

seancorfield06:03:15

(s/def :ok/http-status #{200})

(s/def ::good-response (s/merge ::act/activity
                                (s/keys :req-un [:ok/http-status])))

seancorfield06:03:45

Another Expectations/Spec test at work:

(s/def ::notifiable #{:ok :last})

(defexpect user-should-receive-tests
  (let [db-spec (-> test-system :database :pooled-db)
        peter   (field/as-user db-spec 1)]
    (expecting "immediate yes"
      (expect ::notifiable
              (from-each [note [:like :message :view]]
                (let [field (get settings/legacy-settings note)]
                  (sut/user-should-receive test-system (assoc peter field 1 :searchable true)
                                           :now note)))))
...

seancorfield06:03:34

There's #C2L9MU2RY if you get stuck...

Joseph Graham06:03:20

Yes. the README doesn't justify why Expectations is better than raw clojure.test . But I guess I'll have to try it and find out.

Joseph Graham06:03:39

Also Sean I already use so many of your libraries you're taking over my codebase XD

seancorfield06:03:50

Well, I didn't write the original Expectations or HoneySQL (or tools.cli or java.jdbc or java.data...) - I just took over maintenance since they were useful libraries:grin:

seancorfield06:03:20

I'll try to improve the Expectations readme...

nice 1
Noah Bogart13:03:51

I don’t think ChatGPT is right here (or ever) except by accident, so i won’t speak to that. I think are is an anti-pattern. It loses line numbers, it isn’t any more readable than doseq with is, and is prone to gotchas (accidentally nesting is calls). Expectations is a good library (i use it extensively) but i personally find doseq more readable than from-each.

Joseph Graham13:03:56

Hey I trained chatgpt to convert HugSQL to HoneySQL the other day. Probly svaed me about 2 hours.

Noah Bogart14:03:24

i'm glad it worked out for you. i think it's wrong here to say that using a function is un-idiomatic. are is simply a templating macro, and there are no assumptions about how it should be used.

Joseph Graham15:03:30

Yes I didnt agree with it on that either.

seancorfield16:03:39

@U043HLWSYUQ Re: README -- I'm curious if anything on this page would satisfy the "why is this better than raw clojure.test?" question: https://clojure-expectations.github.io/index.html If so, I'll add some of those examples.

seancorfield16:03:37

(I try hard not to say why my library is "better" than someone else's but I can see why potential users might want that stated, rather than just reading examples and inferring it)

Joseph Graham16:03:52

I guess the stuff in the "More" section does explain the additional features

Noah Bogart16:03:04

I think the primary issue is that "clojure.test for Expectations" expects (heh) the reader to know what Expectations is and why it's useful and then why it would be doubly useful to have with "clojure.test"

Noah Bogart16:03:33

> This library brings expect, more, more-of, etc from Expectations into the clojure.test world to be used instead of (or in addition to) the familiar is macro. without knowledge of those macros, what does this actually do for the user?

seancorfield16:03:05

Fair enough. I'll open an issue...

Joseph Graham16:03:53

the introduction contains a bit > ... and this might be one if the biggest advantages expectations has over clojure.test ... https://clojure-expectations.github.io/introduction.html

Joseph Graham17:03:43

Cool. Maybe once I learn Expectations I can help write something up.

Noah Bogart17:03:29

Off the top of my head, a pitch paragraph might say something like: > The library Expectations is a test framework that includes many useful assertion macros. However, it is incompatible with Clojure's built-in testing library clojure.test, making it hard to integrate with existing tooling. > This is an implementation of the assertions from Expectations without the baggage of replacing clojure.test. expect is multi-faceted, doing x, y and z. in and from-each can do destructuring and looping directly within an assertion. etc etc

Noah Bogart17:03:50

and then the actual examples can be specific and descriptive

Noah Bogart17:03:46

rereading the whole README, i think it actually contains most of this already, it's just spread out and piecemeal. Adding a more descriptive intro to the top would cover that ground nicely

seancorfield17:03:11

Good feedback -- thank you, both!

👍 1
Noah Bogart17:03:57

glad to help! i've put expectations to great use in splint