如何以一对多的方式组合clojure.test.check生成器?

Ste*_*gle 5 testing clojure generative-testing

我们假设我有一个生成器users-gen,它生成一组1个或更多用户.另一个参数化生成器调用user-actions-gen,它接受一个或多个用户的序列,并生成这些用户可能执行的一系列操作.

(def user-gen 
 ;; generates a user
 ...)

(def users-gen 
 ;; sequences of 1 or more users 
 (gen/such-that not-empty (gen/vector gen/users))

(defn user-actions-gen [users]
  ;; a action performed by some user from the `users argument
  ...)
Run Code Online (Sandbox Code Playgroud)

如果我想为users-gen生成的单个用户序列生成单个动作,那么它很简单,只需直接将gen/bind user-gen转换为user-actions-gen.

但是,我想从相同的用户序列生成许多操作.我有这个问题,因为我基本上只是想说"这是状态,让任何随机动作进来,让我们将动作应用到状态,让我们确认状态仍然有效;为所有动作执行此操作. " 我有以下代码.

(defspec check-that-state-is-always-valid
  100
  (let [state-atm (atom {})]
    (prop/for-all
     [[actions users]
      (gen/bind users-gen
                (fn [users]
                  (gen/tuple
                   (gen/vector (user-actions-gen users))
                   (gen/return users))))]
     (doseq [action actions
             :let [state (swap! state-atm state-atm-transform-fx action)]]
       (is (state-still-valid? state))))))
Run Code Online (Sandbox Code Playgroud)

这种作品.问题是:

  1. 它似乎完全评估剂量而不是停止第一个错误
  2. 它看起来有点不对劲.代码遍布各处,它的作用并不完全明显.
  3. 似乎用户动作gen应该采用用户生成器,而不是用户生成的用户价值?这有助于可组合性吗?请注意,我不想将它们放在一起,因为user-gen可能对其他生成器有用.

所以,回顾一下.我从一个生成器获取一个生成的值,并将其作为参数传递给多个生成器.我如何以更有吸引力/更优雅的方式做到这一点?

man*_*nge 1

我将对您当前正在做的事情进行两项主要更改:

  1. 将内联取出gen/bind到一个新的生成器中,暂时命名(另请注意,我交换了结果中和 的users-and-actions-gen顺序以匹配名称):usersactions

    (def users-and-actions-gen
      (gen/bind users-gen
                (fn [users]
                  (gen/tuple
                   (gen/return users)
                   (gen/vector (user-actions-gen users))))))
    
    Run Code Online (Sandbox Code Playgroud)
  2. 不要使用原子来测试不需要它的东西。您可以生成一个惰性状态序列,然后测试它们是否都具有您正在寻找的属性。这样它就会很好地读取并且具有您正在寻找的短路属性:

    (defspec check-that-state-is-always-valid
      100
      (prop/for-all [[users actions] users-and-actions-gen]
        (is (every? state-still-valid? 
                    (reductions (fn [state action]
                                  (state-atm-transform-fx state action))
                                {} actions)))))
    
    Run Code Online (Sandbox Code Playgroud)

除了这两个更改之外,我不确定您是否可以真正改进您正在做的事情。我认为你的users-and-actions-gen描述相当具体。它可以稍微概括,但我不确定概括是否有用(它本质上是一个受限的bind)。我上面提出的建议应该可以解决您的问题(1)和(2),但我认为(3)并不是真正的问题。