Hello, I would really appreciate feedback on my API design. I am integrating with a Java test framework that requires that I pass it an object with user-determined methods (used via reflection; there is no interface defining them). I also want to be able to supply options to the test framework. This is a macro (wrapping gen-class
& more) that I came up with to do this:
(deffixture
"math.algebra.AdditionFixture"
[add subtract] ;; expose these two functions as methods on the class instances
{:concordion/full-ognl false ;; some options for the test framework, optional
:concordion/fail-fast-exceptions [IndexOutOfBoundsException]
::before-suite #(println "AdditionFixture: I run before each Suite") ;; setup & teardown functions, optional
::before-spec #(println "AdditionFixture: I run before each Spec")
::before-example #(println "AdditionFixture: I run before each example")
::after-example #(println "AdditionFixture: I run after each example")
::after-spec #(println "AdditionFixture: I run after each Spec")
::after-suite #(println "AdditionFixture: I run after each Suite")})
I am especially unsure about the setup/teardown functions. Perhaps I should instead pass them along with the methods, relying on well-known names, i.e. [add subtract beforeSuite ... afterSuite]
? Thoughts?
(You can see the code here https://github.com/holyjak/clj-concordion/blob/0.0.3/src/clj_concordion/core.clj#L234 - it does gen-class
, defn
for each of the methods, deftest
so that we can run it via clojure.test, and adds an _opts
method to the class for passing the options map around)I assume you need gen-class instead of proxy because you need to be able to refer to the class by name, as well as extending a concrete base class?
@holyjak what would it look like if you used proxy and deftype instead of genclass? is there something that definitely wouldn't work?
I need to add custom methods, not described by any interface. The only construct that supports that is gen-class, I believe?
I wonder if defining an interface in order to use its methods is weirder than generating gen-class and functions from a macro
it's a legitimate question to me, I find gen-class frustrating enough that I often opt to just making a shim class in a .java file because that's simpler and more readable than the hoops I go through in clojure
I guess it could work just as well. I would still likely need a macro, though, to save the user from repeating the details... I will look into it.
To clarify: 1. I need a class with custom methods (so that the Java framework can invoke them via reflection, for good reasons) 2. I need to associate extra static data with the class (namely test configuration, setup functions etc) 3. I likely want the class named so that, given a file path, I can translate it to the corresponding fixture class and instantiate it. 4. The methods on the class could actually be static, I guess reflection would find them anyway and there is no dynamic state 5. The instances could be singletons, I will ever need just one instance of a fixture class I've considered using just namespaces as they generate classes with static methods but 1) users would need to remember adding :gen-class to ns and prefix functions with - (ie -add though the test uses just "add") 2) I couldn't Spec valid option and extra fn names such as before-suite