什么是Clojure的生成测试?

时间:2017-04-11 14:14:19

标签: clojure functional-programming clojurescript clojure.spec generative-testing

我遇到了Generative Testing in Clojure with spec概念,想了解它。

另外提供一些例子非常有用。

2 个答案:

答案 0 :(得分:12)

作为介绍性阅读,我们得到Rationale and Overview以及Guide,它应该为您提供有关原因和方式的信息。

如果您想要一个有点复杂的示例,我们可以使用string->semantic-version的{​​{1}}函数:

leiningen.release

它需要一个字符串并尝试将其解析为一个程序可读的映射,表示某个工件的版本号。它的规格可能如下:

首先是一些依赖

(defn string->semantic-version [version-string]
  "Create map representing the given version string. Returns nil if the
  string does not follow guidelines setforth by Semantic Versioning 2.0.0,
  http://semver.org/"
  ;; <MajorVersion>.<MinorVersion>.<PatchVersion>[-<Qualifier>][-SNAPSHOT]
  (if-let [[_ major minor patch qualifier snapshot]
           (re-matches
            #"(\d+)\.(\d+)\.(\d+)(?:-(?!SNAPSHOT)([^\-]+))?(?:-(SNAPSHOT))?"
            version-string)]
    (->> [major minor patch]
         (map #(Integer/parseInt %))
         (zipmap [:major :minor :patch])
         (merge {:qualifier qualifier
                 :snapshot snapshot}))))

然后是辅助宏

(ns leiningen.core.spec.util
  (:require
   [clojure.spec           :as spec]
   [clojure.spec.gen       :as gen]
   [miner.strgen           :as strgen]
   [clojure.spec.test      :as test]
   [leiningen.release      :as release]))

后面是语义版本的定义

(defmacro stregex
  "Defines a spec which matches a string based on a given string
  regular expression. This the classical type of regex as in the
  clojure regex literal #\"\""
  [string-regex]
  `(spec/with-gen
     (spec/and string? #(re-matches ~string-regex %))
     #(strgen/string-generator ~string-regex)))

和一些帮助规范

(spec/def ::semantic-version-string
  (stregex #"(\d+)\.(\d+)\.(\d+)(-\w+)?(-SNAPSHOT)?"))

用于在结果地图中定义键

(spec/def ::non-blank-string
  (spec/and string? #(not (str/blank? %))))
(spec/def ::natural-number
  (spec/int-in 0 Integer/MAX_VALUE))

和地图本身

(spec/def ::release/major     ::natural-number)
(spec/def ::release/minor     ::natural-number)
(spec/def ::release/patch     ::natural-number)
(spec/def ::release/qualifier ::non-blank-string)
(spec/def ::release/snapshot  #{"SNAPSHOT"})

后跟功能规范:

(spec/def ::release/semantic-version-map
  (spec/keys :req-un [::release/major ::release/minor ::release/patch
                      ::release/qualifier ::release/snapshot]))

现在我们可以让Clojure Spec生成测试数据并将其提供给函数本身,以测试它是否满足我们为它设置的约束:

(spec/fdef release/string->semantic-version
           :args (spec/cat :version-str ::release/semantic-version-string)
           :ret  ::release/semantic-version-map)

这告诉我们,在为我们生成的1000个测试用例中,函数通过了每一个。

答案 1 :(得分:6)

在潜入 DATE HOUR LCU MVS ACTIVE NUMBER SYSTEM RATE ID 2017-04-03 0 004D PROD 12.15 2017-04-03 0 005F PROD 9.82 2017-04-03 0 0060 PROD 5.99 之前,您可能会发现最容易开始查看 DATE HOUR LCU MVS ACTIVE NUMBER SYSTEM RATE ID 2017-04-03 0 004D PROD 12.15 2017-04-03 0 005F PROD 9.82 2017-04-03 0 0060 PROD 5.99 From the project page:

DATE,HOUR,LCU,MVS,ACTIVE    
,,NUMBER,SYSTEM,RATE        
,,,ID,                      
2017-04-03,0,004D,PROD,12.15
2017-04-03,0,005F,PROD,9.82 
2017-04-03,0,0060,PROD,5.99 
  

在散文中,这个测试读取:对于所有整数向量,v,排序v等于排序v两次。

     

如果我们的测试失败会怎样? test.check将尝试找到仍然失败的“较小”输入。这个过程称为收缩。   让我们看看它的实际效果:

clojure/test.check