Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid having to manually evaluate defrecords and defprotocols while unit testing in Clojure?

Tags:

clojure

In my Clojure codebase, I have defined several protocols and several defrecords. I am using clojure.test to unit test the concrete functions defined within my defrecords.

For example, let's say I have the following source files:

In src/foo/protocols.clj:

(ns foo.protocols)

(defprotocol RequestAcceptability
  (desired-accepted-charset [this]))

In src/foo/types.clj:

(ns foo.types
  (:use [foo.protocols :only [RequestAcceptability desired-accepted-charset]])

(defrecord RequestProcessor
  [field-1 field-2]
  RequestAcceptability
  (desired-accepted-charset [this]
    ...implementation here...))

In test/foo/types_test.clj:

(ns foo.types-test
  (:use [clojure.test])
  (:use [foo.protocols :only [RequestAcceptability desired-accepted-charset]])
  (:import [foo.types RequestProcessor]))

(deftest test-desired-accepted-charset_1
  ...test code here...)

I am using Clojure 1.4, Leiningen 2, nrepl within Emacs.

The annoyance I am facing is that when I go to run my unit tests (e.g., using C-c C-, sequence), I get a ClassNotFoundException: foo.types.RequestProcessor. To get around this, I'm doing the manual work of evaluating, individually, each of my protocol and defrecord forms. I.e., I'll navigate over to my protocols.clj and evaluate (C-M-x key sequence for nrepl) my defprotocol form; and then I'll navigate to my types.clj and evaluate my defrecord form; and then finally I'm able to successfully run my unit tests w/out getting the ClassNotFoundException.

Of course, in my real code base, I have to do this for all my protocols and defrecords, and so it is very tedious and time consuming. Also, if I simply drop to a shell and do lein test, I get the same ClassNotFoundException.

Is there a better way?

Thank you for your time and help.

like image 759
Paul Evans Avatar asked Jan 16 '13 16:01

Paul Evans


2 Answers

You need to require the namespace that contains the defrecord in your test namespace.

(:import [foo.types RequestProcessor])) won't work on its own, since that only works for Java classes that already exist on the classpath. This isn't the case here, since you are using defrecord to dynamically create the class at runtime.

import is usually only needed for Java interop.

like image 159
mikera Avatar answered Sep 28 '22 03:09

mikera


defrecord dynamically generates the class when it is run. Once you go into the namespace and run the code, then the class comes into being and the tests load normally because the class now exists. If you use the namespace from within your tests in addition to importing the class does it load properly for you?

like image 41
Arthur Ulfeldt Avatar answered Sep 28 '22 02:09

Arthur Ulfeldt