oracle-samples/clara-rules

Unit test question

victorrodrigueznadq opened this issue · 5 comments

I'm writing some unit test for my clara rules, however, I only want to test one rule at a time. This is how I'm creating my session:

(def fresh-session (-> (mk-session 'com.nasdaq.rms.rules.nqe.transactional.accumulations.nqe-rms-accumulation-rules-xlsx)
                       ;; insert BillingDates for use in session
                       (insert jan-2024-billing-dates)))

When I create the session, is there a way that I can load only one rule? Or maybe unload all but the rule I'm testing? The above will load all the rules in the specified package. Hoping to be able to just load one at a time for testing. Thanks!

@victorrodrigueznadq,
yes the mk-session macro supports the idea of loading rules piecemeal.
For instance, you should be able to load a session using something like:

(ns some-namespace
  (:require [clara.rules :as r]))

(r/defrule a-rule
  [?o <- Object]
  =>
  (println ?o))
(ns test-some-namespace
 (:require 
   [clara.rules :as r] 
   [some-namespace :as subject]))
   
 (def session 
   (-> (r/mk-session [subject/a-rule])
         (r/insert <items>)
         (r/fire-rules)))

I will note that mk-session also supports a data contract using the same pattern:

(r/mk-session 
  [{:ns-name 'some-namespace,
     :lhs [{:type java.lang.Object, :constraints [], :fact-binding :?o}],
     :rhs `(do (println ~'?o)),
     :name "some-namespace/a-rule"}])

in the off chance that you might want to generate rules on the fly.

thanks @EthanEChristian ! is the r/ syntax required? I'm currently not using that syntax.

@victorrodrigueznadq, no aliasing isn't required. It usually comes down to style preference, unless two namespaces would have overlapping var definitions.

I tend to lean towards alias over refer as I feel it lends to the readability of larger files, and allows for easier distinction of locally defined vars versus those that are imported.

@EthanEChristian sounds good. is there anything I have to do to destroy/unallocate/clear a session so that there's no danger of test intermingling their sessions?

It comes down to how you wish to structure your tests i'd say, for larger sessions or sessions with large numbers of rules that could become expensive to load and reload, i generally encourage using a fixture to bind the generated session within the scope of the namespace tests. Something like:

(def ^:dynamic *session* nil)

(defn session-fixture [f]
  (binding [*session* (r/mk-session [rule-1 rule-2 ....] :cache false)] 
    (f)))
    
(use-fixtures :once session-fixture)
    
(deftest test-scenario-1 
   (let [fired-session (-> *session* (r/insert <scenario-1-fact>) r/fire-rules)] 
      (<preform assertions>))

(deftest .....)
(deftest .....)

For more piecemeal testing, i'd probably just bind the specific session within the bounds of the deftest itself. Something like:

(deftest test-scenario-1 
  (let [fired-session (-> (r/mk-session [specific-rule] :cache false) (r/insert <scenario-1-fact>) r/fire-rules)] 
      (<preform assertions>))
      
(deftest .....)
(deftest .....)