Share your thoughts in the 2021 Clojure Community Survey!

Welcome! Please see the About page for a little more info on how this works.

0 votes
in Test by

Happens when I want to test a function where the result is another function. I have something like this:

  ns flexsearch.core

    (defn init [{:keys [tokenizer split indexer filter] :as options}]
  (let [encoder (get-encoder (:encoder options))]
    (assoc (merge {:ids {} :data {}} options)
           :indexer (get-indexer indexer)
           :encoder encoder
           :tokenizer (if (fn? tokenizer) tokenizer #(string/split % (or split #"\W+")))
           :filter (set (mapv encoder filter)))))

And in the test ns:

ns flexsearch.core-test
[flexsearch.core :as f]

(def split #"\W+")

(is (= (f/init {:tokenizer false :split split :indexer :forward :filter #{"and" "or"}})
         {:ids {},
          :data {},
          :tokenizer f/init/fn--14976,
          :split #"\W+",
          :indexer f/index-forward,
          :filter #{"or" "and"},
          :encoder f/encoder-icase}))

the result in the repl is:

{:ids {},
 :data {},
 :tokenizer #function[flexsearch.core/init/fn--14976],
 :split #"\W+",
 :indexer #function[flexsearch.core/index-forward],
 :filter #{"or" "and"},
 :encoder #function[flexsearch.core/encoder-icase]}

I know that I have to put f/index-forward instead of the result of the repl [flexsearch.core/index-forward], but it doesn't work with f/init/fn--14976 (No such var: f/init/fn--14976)

I supouse that is a trick with the vars but i dont know how it really works. Any reading you can provide i will be gratefull

---EDIT--- The f/index-forward and f/encoder-icase notations works fine.

---EDIT 2--- i've defined:

(defn spliter [split]   (fn [x] (string/split x (or split #"\W+"))))
and used it on:

(defn init [{:keys [tokenizer split indexer filter] :as options}]
  (let [encoder (get-encoder (:encoder options))]
    (assoc (merge {:ids {} :data {}} options)
           :indexer (get-indexer indexer)
           :encoder encoder
           :tokenizer (if (fn? tokenizer) tokenizer (spliter split))
           :filter (set (mapv encoder filter)))))

the I get a similar ":tokenizer #function[flexsearch.core/spliter/fn--34857]," that I used in the test and it also failed –

1 Answer

+2 votes
by

You can' t compare functions for equality so I think you should investigate alternatives to that strategy in your test. You can test functions for identity, but that is generally a very fragile test.

by
I was told of that, but why (= + +) is true for example? or the problem is with the 'is' in test?. And... on the other hand I did this test and it went ok.

(defn get-encoder [encoder]
  (case encoder
    :icase encoder-icase
    :simple encoder-simple
    :advanced encoder-advanced
    encoder-icase))

(deftest get-encoder
  (is (fn? (f/get-encoder :icase)))
  (is (= (f/get-encoder nil) f/encoder-icase))
  (is (= (f/get-encoder :icase) f/encoder-icase))
  (is (= (f/get-encoder :simple) f/encoder-simple))
  (is (= (f/get-encoder :advanced) f/encoder-advanced)))

what you mean for test functions for identity and why is fragile? sorry if my questions are silly it's my first language. can you recomend any reading about this?

thanks for your time alex!
by
In `(= + +)` you're seeing an identity check (identical function object being compared). Functions are weird in this respect in that they compare equal only on identity, but there are a lot of ways you might see two functions that do the same thing but are not identical. It's just generally not something I would trust in a test.
by
okk, but still i dont understand why it works with (= (f/get-encoder nil) f/encoder-icase) and no with (=  #(string/split % (or split #"\W+"))   f/init/fn--14976) if they are exactly the same functions too
by
Each call to the anonymous function #(string/split % (or split #"\W+")) returns  a NEW different (in terms of identity) function, even though, functionally, it does the same thing.

Also, the f/init/fn--14976 is just what the REPL prints as the function.  In this case, since it is an anonymous function, it is not bound to a symbol, even though it may look like it is.  In the case of f/encoder-icase, the function is bound to the symbol f/encoder-icase.  Which is why it "works".  As mentioned by alexmiller, testing for function identity is fragile at best.  Testing for equality is undecidable in general.
by
The concept became clearer...Thanks Dorab and Alex!
...