testing

2021-08-10T17:44:07.024700Z

is there any good method for mocking something like java.util.Date. ? I am creating a map with a :date entry and don’t want to have to worry about what it actually is when testing against the other contents

2021-08-10T17:44:30.025300Z

i know i could write (map #(dissoc % :date)) in my test but that’s so cumbersome, especially when writing multiple tests

2021-08-10T17:45:35.026300Z

i’d prefer to just write (= [{:date nil :text "Hello!"}] (-> (sut/new-game) (sut/add-message "Hello!") (:messages)))

2021-08-10T17:49:06.027400Z

i just had the thought that a simple (defn new-date [] (java.util.Date.)) allows me to use with-redefs , but is that doesn’t seem generalizable. It will work for now so I’m not going to spend more time on this, but i’m still curious

vemv 2021-08-10T17:50:15.028400Z

Date. is akin to a side-effect. Some call it a side-cause or "coeffect"

👍 1
2021-08-10T17:50:28.028900Z

if your code depends on the clock, then you should consider giving it a now parameter

vemv 2021-08-10T17:50:54.029400Z

^ exactly, that way you seggregate the side-cause

2021-08-10T17:51:02.029900Z

lol, side-cause is a great name

2021-08-10T17:52:24.031400Z

interesting. so how do i pass it in? i hope to allow the engine (for a game) to add messages from anywhere, which makes me think it’ll be cumbersome to have to call (java.util.Date.) every time I want to add a message

2021-08-10T17:52:42.032Z

once I’ve done it a couple times i start thinking “okay, how do I refactor to remove duplication?”

2021-08-10T17:53:00.032400Z

you could have a little message constructor that captures the clock

2021-08-10T17:53:29.033Z

(defn messasge [m] {:msg m :time (java.util.Date)})

2021-08-10T17:54:07.033500Z

wow, I'm completely incapable of typing today

2021-08-10T17:55:28.035Z

so I would call (add-message game (message "Hello!")) in the engine instead of (add-message game "Hello!")? and in a test I can just pass in (sut/add-message game {:date nil :text "Hello!"})?

2021-08-10T17:55:58.035800Z

sure, that's reasonable

2021-08-10T17:56:32.037Z

seems like all i’ve done is move the (java.util.Date.) from within add-message to message. maybe I don’t understand enough, or maybe I haven’t done a good enough job of showing my code up front lol

2021-08-10T17:56:37.037200Z

classic X/Y problem

2021-08-10T17:56:44.037600Z

(defn add-message [game message]
  (update game :messages conj {:date (java.util.Date.)
                               :text message}))

2021-08-10T17:56:57.038100Z

what are you trying to do by putting a nil in for the date?

2021-08-10T17:57:29.038700Z

allow me to test against the whole :messages vector without having to deliberately exclude the :date key in my equality checks

👍 1
pithyless 2021-08-16T19:26:11.051600Z

Hey @nbtheduke - sorry for resurrecting a week-old thread, but I think this is some real insight that may have been overlooked. > allow me to test against the whole `:messages` vector without having to deliberately exclude the `:date` key in my equality checks Irrespective of whether you want to have an external clock passed to functions (so you can test and easily control behavior based on time); if what you want to do is check equality without dates; then that is code that should exist somewhere directly in the test codebase:

(is (= expected-msgs (elide-dates generated-msgs)))
How you implement eldie-dates is up to you; it could be as simple as a helper function that does dissoc on each message in the collection, or it could use some fancy testing matching library (ala https://github.com/nubank/matcher-combinators) or some fancy data wrangling library (ala https://github.com/noprompt/meander or https://github.com/redplanetlabs/specter) or some fancy query library (ala https://github.com/lilactown/autonormal)

2021-08-16T19:31:49.052Z

ha I wrote get-messages which does what your elide-dates does: (defn get-messages [game] (mapv #(dissoc % :date) (:messages game))) . Glad to know i’m not too far off

Jacob Emcken 2022-03-09T15:57:53.311319Z

This is an alternative which actually mocks "creating a date", whether it is better or worse 🤷

Jacob Emcken 2022-03-09T15:58:49.700349Z

(defn now []
  (java.util.Date.))
  
(def freezed-time #inst "2022-03-09T15:54:02.699-00:00")
  
(with-redefs [now (constantly freezed-time)]
  (= (now) #inst "2022-03-09T15:54:02.699-00:00")) => true

👍 1
2022-03-09T15:59:41.343049Z

That’s a good one too!

Jacob Emcken 2022-03-09T16:01:47.012229Z

If you ever start using the "newer" (immutable) dates in Java: https://github.com/dm3/clojure.java-time They have a built-in with-clock that can help you mock time in tests:

(with-clock (system-clock "UTC")
  (zoned-date-time 2015 10))
=> #<java.time.ZonedDateTime 2015-10-01T00:00Z[UTC]>

2021-08-10T17:57:56.039100Z

ahh, I see. In that case, moving it does have some value.

👍 1
2021-08-10T17:58:32.040Z

Another possibility, which I kind of hesitate to mention: if two messages are logically equal even if the dates are different, then perhaps the date should be metadata.

2021-08-10T17:59:10.040400Z

but it sounds like that's not the case, instead it's just for the test assertion.

vemv 2021-08-10T18:00:30.041700Z

defn add-message [game message {:keys [date] :or {:date (Date.)}}]
poor man's DI / Functional Architecture

2021-08-10T18:00:55.042100Z

oh! that’s clever