Unit testing clojure:优雅地模拟具有不同值的函数

Unit testing clojure:优雅地模拟具有不同值的函数,unit-testing,clojure,Unit Testing,Clojure,在编写单元测试时,我有时不得不模拟一个函数,以便为每个函数调用返回一系列定义的值 目前我正在做这样的事情: (testing "fitness-proportionate selection" ; store sequence of rand values in atom, first val is ignored as we always use rest ; normalized fitness: 0: 0 1: 1/6 2: 1/3 3: 1/2 ; summed fitnes

在编写单元测试时,我有时不得不模拟一个函数,以便为每个函数调用返回一系列定义的值

目前我正在做这样的事情:

(testing "fitness-proportionate selection"
  ; store sequence of rand values in atom, first val is ignored as we always use rest
  ; normalized fitness: 0: 0 1: 1/6 2: 1/3 3: 1/2
  ; summed fitness 0: 0 1: 1/6 2: 1/2 3: 1
  (let [r (atom [0 1 1/2 1/6 0])] (with-redefs [rand (fn [] (first (swap! r rest)))]
    (is (= [3 2 1 0] (fitness-proportionate-selection [0 1 2 3] identity))))))
有人能帮我找到一个更优雅的方法吗? 可读性更强,逻辑性更少的东西。 这将减少单元测试本身的错误。
我目前使用的是
clojure.test
,我不想使用额外的库。

除了使用某种引用保存它应该返回的值序列之外,我想不出其他方法来模拟
rand
函数。这是有意义的,因为
rand
本身就是一个从其他(伪随机)源生成其值的函数

也就是说,我将创建一个高阶函数,该函数返回基于数字序列的数字生成器,而不是将该逻辑嵌入测试代码中

(defn gen-rand
  "Returns a no args function that mocks `rand`,
  which returns on each call a number from s in 
  the same order provided."
  [s]
  (let [x (atom s)]
    #(let [n (first @x)] 
       (swap! x rest)
       n)))

(defn fitness-proportionate-selection
  "Mock function."
  [s f]
  (vec (repeatedly 4 rand)))

(testing "fitness-proportionate selection"
  (with-redefs [rand (gen-rand [1 1/2 1/6 0])]
    (is (= [1 1/2 1/6 0] (fitness-proportionate-selection [0 1 2 3] identity)))))

请注意,我更改了代码,以便返回提供给
gen rand
的序列中的所有值,并且不会丢弃第一个值。

这是一个很好的解决方案。我认为值得指出的是,上面编写的
gen rand
函数允许您模拟任何0-arity函数以按顺序返回一系列值,只需简单的调整,它就可以模拟任何arity函数。这在测试中是一个很好的助手函数。