Skip to content

Commit 2e35883

Browse files
author
Vadim Platonov
committed
add mock clock
1 parent 4e78f23 commit 2e35883

File tree

6 files changed

+130
-2
lines changed

6 files changed

+130
-2
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## 0.3.1
4+
5+
### New Features
6+
7+
* `clock?` predicate
8+
* `mock-clock` - returns a mocked instance of `java.time.Clock`.
9+
310
## 0.3.0
411

512
### Breaking changes

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,25 @@ the `with-clock` macro and the corresponding `with-clock-fn` function:
426426
=> #<java.time.ZoneRegion UTC>
427427
```
428428

429+
In addition to the built-in `java.time` clocks, we provide a Mock clock which
430+
can be very handy in testing:
431+
432+
```clojure
433+
(def clock (mock-clock 0 "UTC"))
434+
=> #'user/clock
435+
436+
(with-clock clock
437+
(j/instant))
438+
=> #object[java.time.Instant "1970-01-01T00:00:00Z"]
439+
440+
(advance-clock! clock (plus (hours 5) (minutes 20)))
441+
=> nil
442+
443+
(with-clock clock
444+
(j/instant))
445+
=> #object[java.time.Instant "1970-01-01T05:20:00Z"]
446+
```
447+
429448
Clock overrides works for all of the date-time types.
430449

431450
#### Fields, Units and Properties

src/java_time.clj

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
(:require [java-time.potemkin.namespaces :as potemkin]
44
[java-time.util :as jt.u]
55
[java-time core properties temporal amount zone single-field local chrono
6-
convert sugar seqs adjuster interval format joda clock pre-java8]))
6+
convert sugar seqs adjuster interval format joda clock pre-java8 mock]))
77

88
(potemkin/import-vars
99
[java-time.clock
@@ -39,10 +39,13 @@
3939
[java-time.zone
4040
available-zone-ids zone-id zone-offset
4141
offset-date-time offset-time zoned-date-time
42-
system-clock fixed-clock offset-clock tick-clock
42+
system-clock fixed-clock offset-clock tick-clock clock?
4343
zoned-date-time? offset-date-time? offset-time?
4444
with-zone-same-instant with-offset with-offset-same-instant]
4545

46+
[java-time.mock
47+
mock-clock advance-clock!]
48+
4649
[java-time.convert
4750
as-map convert-amount to-java-date to-sql-date to-sql-timestamp
4851
to-millis-from-epoch]

src/java_time/mock.clj

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
(ns java-time.mock
2+
(:require [java-time
3+
[core :as core]
4+
[temporal :as temporal]
5+
[zone :as zone]])
6+
(:import (java.time Clock)))
7+
8+
(definterface IMockClock
9+
(^void advanceClock [amount]))
10+
11+
(defn ^Clock mock-clock
12+
"Returns a mock implementation of the `java.time.Clock`. The mock supports
13+
`advance-clock!` operation which allows to move the time in the clock, e.g.:
14+
15+
(let [clock (mock-clock 0 \"UTC\")]
16+
(with-clock clock
17+
(is (= (value clock) 0))
18+
(is (= (instant) (instant 0)))
19+
(advance-clock! clock (j/millis 1))
20+
(is (= (value clock) 1))
21+
(is (= (instant) (instant 1)))))
22+
23+
You can move the clock back via advancing by a negative temporal amount.
24+
25+
Creates a clock at epoch in the default timezone when called without arguments."
26+
([] (mock-clock 0))
27+
([instant] (mock-clock instant (zone/zone-id)))
28+
([instant zone]
29+
(let [!instant (atom (temporal/instant instant))
30+
zone (zone/zone-id zone)]
31+
(proxy [Clock IMockClock] []
32+
(advanceClock [amount]
33+
(swap! !instant core/plus amount)
34+
nil)
35+
36+
(getZone [] zone)
37+
(withZone [zone']
38+
(mock-clock @!instant zone'))
39+
(instant [] @!instant)
40+
41+
(equals [other]
42+
(if (and (instance? IMockClock other) (zone/clock? other))
43+
(let [^Clock clock other]
44+
(and (= zone (.getZone clock))
45+
(= @!instant (.instant clock))))
46+
false))
47+
(hashCode []
48+
(bit-xor (hash @!instant) (hash zone)))
49+
(toString []
50+
(str "MockClock[" @!instant "," zone "]"))))))
51+
52+
(defn advance-clock!
53+
"Advances the `clock` by the given time `amount`.
54+
55+
This mutates the mock clock."
56+
[^IMockClock clock amount]
57+
(.advanceClock clock amount))

src/java_time/zone.clj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,10 @@
408408
([d] (Clock/tick (system-clock) (jt.a/duration d)))
409409
([^Clock c, d] (Clock/tick c (jt.a/duration d))))
410410

411+
(defn clock?
412+
"Returns true if `x` is an instance of `java.time.Clock`."
413+
[x] (instance? Clock x))
414+
411415
(extend-type Clock
412416
jt.c/ReadableProperty
413417
(value [c] (.millis c))

test/java_time_test.clj

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,44 @@
441441
(is (j/after? (j/month-day 5 1) (j/month-day 4 1)))
442442
(is (j/before? (j/month-day 1 1) (j/month-day 4 1)))))
443443

444+
(deftest mock-clock
445+
(testing "constructors"
446+
(is (= (j/mock-clock)
447+
(j/mock-clock 0)
448+
(j/mock-clock 0 (j/zone-id)))))
449+
450+
(let [utc-clock #(j/mock-clock % "UTC")]
451+
(testing "accessors"
452+
(let [clock (utc-clock 0)]
453+
(is (= 0 (j/value clock)))
454+
(is (= (j/zone-id "UTC") (j/zone-id clock)))))
455+
456+
(testing "equality"
457+
(is (= (utc-clock 0) (utc-clock 0)))
458+
(is (= (hash (utc-clock 0)) (hash (utc-clock 0))))
459+
(is (not= (utc-clock 0) (utc-clock 1)))
460+
(is (not= (utc-clock 0) (j/mock-clock 0 "GMT"))))
461+
462+
(testing "advance"
463+
(let [clock (utc-clock 0)]
464+
(testing "by positive amount"
465+
(j/advance-clock! clock (j/millis 1))
466+
(is (= 1 (j/value clock))))
467+
468+
(testing "by negative amount"
469+
(j/advance-clock! clock (j/millis -1))
470+
(is (= 0 (j/value clock))))
471+
472+
(testing "clone with a different zone"
473+
(let [cloned-clock (j/with-zone clock "GMT")]
474+
(is (not (identical? clock cloned-clock)))
475+
(is (= (j/zone-id "GMT") (j/zone-id cloned-clock)))
476+
(is (= (j/value cloned-clock) (j/value clock)))
477+
478+
(j/advance-clock! cloned-clock (j/seconds 1))
479+
(is (= 1000 (j/value cloned-clock)))
480+
(is (not= (j/value cloned-clock) (j/value clock)))))))))
481+
444482
(deftest properties
445483
(testing "units"
446484
(is (= (j/unit :seconds)

0 commit comments

Comments
 (0)