Showing posts with label macro. Show all posts
Showing posts with label macro. Show all posts

Tuesday, February 7, 2017

My First Macro

I'm writing my first test code to cover my HugSQL.  I'm still on my training wheels, so I can't be more fancier here:

-- :name get-user-type :? :*
-- :doc get user by type
select id, name from users where type = :type order by id


Now comes the mythical test code. Let's suppose "db/add-user!" exists, performs database inserts into the users table and returns 1 as return value.

(deftest test-user
  (jdbc/with-db-transaction [t-conn *db*]
    (jdbc/db-set-rollback-only! t-conn)
    (is (= 1 (db/add-user!
              t-conn
              {:username      "u123"
               :type          "admin"})))
    (is (= {:username      "u123"
            :type          "admin"}
           (db/get-user-type t-conn {:type "admin"}))))))

But alas! This is an epic failure!

(expected: {:username "u123",
            :type "admin"}
  actual: ({:id 1,
            :username "u123",
            :type "admin"})

It appears that ":id" value is an auto-generated primary key and this equality test is not going to work.


Perhaps, I could define a function to remove the id before comparing? This might work as the id is a surrogated key automatically populated by the database.

(defn match-record
  [expected record]
  (= expected (dissoc record :id)))


Or perhaps I could raise the bar higher by writing a macro? After all, I've heard Clojure macro is a powerful language extension.

(defmacro match-record
  [expected record]
  `(= ~expected (dissoc ~record :id)))


This is really exciting - my first ever Clojure macro!


So, rewriting my test code gives me,

(deftest test-user
  (jdbc/with-db-transaction [t-conn *db*]
    (jdbc/db-set-rollback-only! t-conn)
    (is (= 1 (db/add-user!
              t-conn
              {:username      "u123"
               :type          "admin"})))
    (let [result (db/get-user-type t-conn {:type "admin"})]
        (is (match-record {:username   "u123"
                           :type       "admin"}
                          (first result))))))



I know my macro is sub-optimal here and there are better ways of doing this. But hey! I'm certain my dear mother will be proud of me!