Post

How to mock Java classes in Clojure

In this post we’re going to show how to use Clojure proxy to mock Java classes and provide own functionality for Java methods.

Proxy class with no-arg constructor

Calls superclass default constructor:

1
2
3
4
5
6
7
(def my-list
  (proxy [ArrayList] []
    (get [i] 42)))

;; Let's use it:
;; user> (.get my-list 10)
;; 42

No matching ctor found for class

Superclass has no no-arg constructor:

1
2
3
(proxy [FileInputStream] []
        (read []
          42))

Produces an exception, because there’s no no-arg constructor in FileInputStream class:

1
CompilerException java.lang.IllegalArgumentException: No matching ctor found for class user.proxy$java.io.FileInputStream$ff19274a

Proxy java class with constructor

We have to provide obligatory constructor parameter (here “/proc/version”), that will be passed to superclass:

1
2
3
4
5
6
7
;; To count calls and return predictive values:
(def counter (atom 0))

(def my-mock
  (proxy [FileInputStream] ["/proc/version"]
    (read []
      (swap! counter inc))))

Now our version of read method may be called as expected:

1
2
3
4
5
6
user> (.read my-mock)
1
user> (.read my-mock)
2
user> (.read my-mock)
3

Overload mocked method

1
2
3
4
5
6
7
8
9
(def counter (atom 0))

(def my-mock
  (proxy [FileInputStream] ["/proc/version"]
    (read
      ([] (swap! counter inc)) ; read()
      ([^bytes arr]            ; read(byte[])
       (aset-byte arr 0 42)
       (swap! counter inc)))))

read(byte[]) can be called like this:

1
2
3
4
user> (.read my-mock (byte-array 3))
1
user> (.read my-mock (byte-array 3))
2

References:

This post is licensed under CC BY 4.0 by the author.