Created
March 16, 2017 09:21
-
-
Save bendisposto/c3456187a018d420262eb3e90035f38b to your computer and use it in GitHub Desktop.
REPL Session - Generative Testing, Softwerkskammer Ruhr, 15.3.2017
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns bsp.core | |
(:require [clojure.repl :refer :all] | |
[clojure.spec :as spec] | |
[clojure.test.check :refer [quick-check]] | |
[clojure.test.check.generators :as gen] | |
[clojure.test.check.properties :refer [for-all]] | |
[clojure.test :refer [is are run-tests deftest]])) | |
(defn ultrasort [coll] (seq (into (sorted-set) coll))) | |
(set! *print-length* 20) | |
;; Clojure is a dialect of the Lisp programming language [...] | |
;; Clojure's focus on programming with immutable values and explicit | |
;; progression-of-time constructs are intended to facilitate the | |
;; development of more robust programs, particularly multithreaded | |
;; ones. (Wikipedia) | |
(comment | |
;; There are numbers | |
3 | |
1.99 | |
2/6 | |
;; There are booleans | |
true | |
false | |
;; There are Strings | |
"Hallo Softwerkskammer" | |
;; Multiline Strings! | |
"Hallo | |
Softwerkskammer" | |
;; There are Keywords (Ruby: Symbols, Java: kinda Enums) | |
:foo | |
;; And we can have namespaced keywords (good names are rare) | |
:trololo/tralala | |
::lol | |
;; There is nil (Java: null) | |
nil | |
;; There are Sequences | |
[1 2 3] ;; Look Ma, no comma! | |
[1,2,,,3] ;; I couldn't care less | |
;; There are Maps | |
{1 2 3 4} | |
;; Everything can have a different type | |
{:key :value 3 "drei"} | |
;; Really ... any type | |
{:a :b, 4 {}, [[] 6] "doh"} | |
;; And finally: THE MIGHTY PARENS | |
(println "Hallo Softwerkskammer") | |
(+ 2 4 5) | |
;; First argument is the Funktion, all other argument are parameters | |
;; Compare: square(4) vs. (square 4) | |
;; Did I mention that Clojure runs on the JVM? | |
(type "Hallo") | |
(type 4) | |
(.toLowerCase "ROFL") | |
;; Now let's write some functions | |
(def square (fn [x] (* x x))) | |
(square 3) | |
;; (def ... (fn ...)) is very popular | |
(defn cube [x] (* x x x)) | |
(cube 3) | |
;; Clojure is a functional language | |
;; Functions are first class | |
(map cube (range 10)) | |
(->> (range 10) | |
(map cube)) | |
(filter even? (range 20)) | |
(defn make-adder [x] | |
(fn [y] (+ x y))) | |
(def add17 (make-adder 17)) | |
(add17 4) | |
;; Do you want to learn more? | |
;; Visit our Dojo, next Thursday (23.3.2017) | |
;; in Düsseldorf: https://goo.gl/Z1bcoI | |
;; You've just learned enough Clojure to participate | |
;; Now let us do some testing | |
;; @edial: | |
;; QA Engineer walks into a bar. | |
;; Orders a beer. | |
;; Orders 0 beers. | |
;; Orders 999999999 beers. | |
;; Orders a lizard. | |
;; Orders -1 beers. | |
;; Orders a sfdeljkn | |
(deftest squaring-3 | |
(is (= 10 (square 3)) "3 squared should be 9")) | |
(deftest ultrasort-simple-cases | |
(are [x y] (= x y) | |
(ultrasort [1 2]) [1 2] | |
(ultrasort [3 1 2]) [1 2 3] | |
(ultrasort [1 3 2]) (ultrasort [3 2 1]))) | |
(run-tests) | |
;; ok ok, now generative testing ... | |
;; what are we doing, when we write tests? | |
;; this is a generator | |
gen/int | |
;; sample takes a generator and asks for some generated values | |
(gen/sample gen/boolean) | |
(gen/sample gen/int 20) | |
;; There are predefined generators for all standard-types | |
(gen/sample gen/nat 15) | |
(gen/sample gen/string-alphanumeric) | |
(gen/sample gen/string) | |
(gen/sample gen/keyword) | |
;; Just give me some valid Clojure data | |
(last (gen/sample gen/any 50)) | |
;; a generator to randomly select from a collection | |
(gen/sample (gen/elements [1 "zwei" [3.0] :vier])) | |
;; a very boring generator | |
(gen/sample (gen/return :gähn)) | |
;; turns a value into a generator that produces that value | |
;; we also have generators for collections | |
(gen/sample (gen/vector gen/int)) | |
(gen/sample (gen/map gen/int gen/string-ascii)) | |
;; a generator for tuples | |
(gen/sample (gen/tuple gen/int gen/boolean gen/nat)) | |
;; a generator that randomly selects from other generators | |
(gen/sample (gen/one-of [gen/int gen/boolean gen/string-alpha-numeric]) 20) | |
;; This is very different from elements! | |
;; a filter | |
(gen/sample (gen/such-that even? gen/int)) | |
;; a filter that removes empty results | |
(def sort-input (gen/not-empty (gen/vector gen/int))) | |
(gen/sample sort-input) | |
;; transformation | |
(gen/sample (gen/fmap square gen/int)) | |
;; this is not the same as (map square (gen/sample gen/int)) | |
;; Guess how long it will take to produce Strings that start with https:// | |
(gen/sample (gen/such-that (fn [s] (.startsWith s "https://")) gen/string)) | |
(gen/sample (gen/fmap (partial str "https://") gen/string-alphanumeric)) | |
(+ 6 3 3 3) | |
(def foo (partial + 17)) | |
(foo 4 5) | |
;; complex combination | |
;; need a vector of some type and a single number taken from the vector | |
(def vector-gen (gen/not-empty (gen/vector gen/any))) | |
(gen/sample vector-gen) | |
(defn vector-element-tuple-gen [v] | |
(gen/tuple (gen/return v) | |
(gen/elements v))) | |
(gen/sample (vector-element-tuple-gen [1 2 3 4 5])) | |
(gen/sample (gen/bind vector-gen vector-element-tuple-gen)) | |
;; For the advanced functional programmer: the names bind and return | |
;; are not chosen by accident | |
;; Specifying properties: | |
;; (for-all [v1 GENERATOR1 v2 GENERATOR2 ...] (PREDICATE v1 v2 ...)) | |
;; Let's test ultrasort, the blazing fast sort for nonempty vectors | |
(def nonempty-gen (gen/not-empty (gen/vector gen/int))) | |
(defn sorted? [v] (apply <= v)) | |
(apply + 1 2 [3 4 5 56]) | |
(+ 1 2 3 4 5 56) | |
(sorted? [1 2 3 4]) | |
(sorted? [1 7 3 4]) | |
(def sorted-prop | |
(for-all [v nonempty-gen] | |
(sorted? (ultrasort v)))) | |
(quick-check 100 sorted-prop) | |
(quick-check 10000 sorted-prop :seed 1472739084232) | |
;; Ok, does ultrasort work? | |
(def equivalence | |
(for-all [v nonempty-gen] | |
(let [v' (ultrasort v)] | |
(= v' (sort v))))) | |
(def r (quick-check 100 equivalence :seed 1489425237298)) | |
(def ce (first (:fail r))) | |
ce | |
(get-in r [:shrunk :smallest]) | |
;; sorted? is not sufficient | |
;; this satisfies the sorted? property: | |
(defn veryquicksort [v] [0]) | |
;; Sorting is finding a permutation of the elements such that sorted? is true | |
(def permutation | |
(for-all [v nonempty-gen] | |
(= (frequencies v) | |
(frequencies (ultrasort v))))) | |
(frequencies [1 2 3 2 1 2 2 2]) | |
(quick-check 100 permutation) | |
(defn ultrasort ;; actually this is quicksort from http://eddmann.com/posts/quicksort-in-clojure/ | |
[[pivot & coll]] | |
(when pivot | |
(concat (ultrasort (filter #(< % pivot) coll)) | |
[pivot] | |
(ultrasort (filter #(>= % pivot) coll))))) | |
(quick-check 1000 permutation) | |
(quick-check 1000 sorted-prop) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defproject bsp "0.1.0-SNAPSHOT" | |
:description "FIXME: write description" | |
:url "http://example.com/FIXME" | |
:license {:name "Eclipse Public License" | |
:url "http://www.eclipse.org/legal/epl-v10.html"} | |
:dependencies [[org.clojure/clojure "1.9.0-alpha14"] | |
[org.clojure/test.check "0.9.0"]] | |
:profiles {:dev {:dependencies [[pjstadig/humane-test-output "0.8.1"]] | |
:injections [(require 'pjstadig.humane-test-output) | |
(pjstadig.humane-test-output/activate!)]}}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment