Contracts in-clojure-pete

101
@jessitron es! Tests! Clojure! Contracts in

Transcript of Contracts in-clojure-pete

Page 1: Contracts in-clojure-pete

@jessitron

Types!Tests!

Clojure!

Contractsin

Page 2: Contracts in-clojure-pete

Contracts and Clojure: the Best-Yet Compromise

Between Types and Tests

Philly ETE, 7 April 2015

Page 3: Contracts in-clojure-pete

What do we know?

How do we know it?

This code works!

Page 4: Contracts in-clojure-pete

Formal Proofs

Informal Reasoning

Experimental Evidence

Page 5: Contracts in-clojure-pete

def formatReport(data: ReportData): ExcelSheet

// Scala

Page 6: Contracts in-clojure-pete

types

Page 7: Contracts in-clojure-pete
Page 8: Contracts in-clojure-pete

(defn format-report [report-data] …)

Page 9: Contracts in-clojure-pete

(defn ad-performance-report [params] (-> (fetch-events params) (analyze-ad-performance params) format-report))

Page 10: Contracts in-clojure-pete

(defn ad-performance-report [params]

(-> (fetch-events params)

(analyze-ad-performance params)

format-report))

Page 11: Contracts in-clojure-pete

(defn analyze-ad-performance [events params] (-> events (group-up params) summarize add-total-row (add-headers params)))

(defn ad-performance-report [params]

(-> (fetch-events params)

(analyze-ad-performance params)

Page 12: Contracts in-clojure-pete

{:when 12:34:56 7/8/90 :what "show" :who "abc123"}

Page 13: Contracts in-clojure-pete

{:when org.joda.time.DateTime :what java.lang.String :who java.lang.String}

(def Event )

Page 14: Contracts in-clojure-pete

(def Event )

{:when DateTime :what s/Str :who s/Str}

(:require [schema.core :as s])

Page 15: Contracts in-clojure-pete

(def Event )

{:when DateTime :what Incident :who Customer}

(:require [schema.core :as s])

Event

Page 16: Contracts in-clojure-pete

(def Event )

{:when DateTime :what Incident :who Customer}

(:require [schema.core :as s])

[Event]

(defn ad-performance-report [params] (-> (fetch-events params) (analyze-ad-performance params)

Page 17: Contracts in-clojure-pete

[Event]

(defn ad-performance-report [params] (-> (fetch-events params) (analyze-ad-performance params) format-report))

Page 18: Contracts in-clojure-pete

[Event]

…)(s/defn fetch-events [params]

Page 19: Contracts in-clojure-pete

[Event]

(:require [schema.core :as s])

…)

(s/defn fetch-events[params]

:-

Page 20: Contracts in-clojure-pete

(deftest fetch-events-test … (= (expected (fetch-events input))))

Page 21: Contracts in-clojure-pete

(deftest fetch-events-test … (= (expected (fetch-events input))))

(use-fixtures schema.test/validate-schemas)

Page 22: Contracts in-clojure-pete

(def Event )

{:when DateTime :what Incident :who Customer}

(:require [schema.core :as s])

[Event]

(defn ad-performance-report [params] (-> (fetch-events params) (analyze-ad-performance params)

Page 23: Contracts in-clojure-pete

(def Event )

{:when DateTime :what Incident :who Customer}

(:require [schema.core :as s])

[Event]

(defn ad-performance-report [params] (-> (fetch-events params) (analyze-ad-performance params)

[[Event]]

Page 24: Contracts in-clojure-pete

[Event]

(defn ad-performance-report [params] (-> (fetch-events params) (analyze-ad-performance params)

[[Event]][Event]

Page 25: Contracts in-clojure-pete

[Event]

(defn ad-performance-report [params] (-> (fetch-events params) (analyze-ad-performance params)

[[Event]]

[Event]

Page 26: Contracts in-clojure-pete

[Event]

(defn ad-performance-report [params] (-> (fetch-events params) (analyze-ad-performance params)

[[Event]]

[[Event] Summation]

Page 27: Contracts in-clojure-pete

[Event]

[( one [Event] ) ( one Summation )]

[[Event]]

Page 28: Contracts in-clojure-pete

[Event]

[(s/one [Event] "event list") (s/one Summation "group sum")]

[[Event]]

Page 29: Contracts in-clojure-pete

(def Group )

[Event]

[(s/one [Event] "event list") (s/one Summation "group sum")]

[[Event]] [Group]

Page 30: Contracts in-clojure-pete

[Event] [[Event]]

{:groups [Group]}

Page 31: Contracts in-clojure-pete

[Event] [[Event]]

{:groups [Group] :totals Totals}

Page 32: Contracts in-clojure-pete

[Event] [[Event]]

{:header Headers :groups [Group] :totals Totals}

Page 33: Contracts in-clojure-pete

[Event] [[Event]]

{:header Headers :groups [Group] :totals Totals}

(def ReportData

)

Page 34: Contracts in-clojure-pete

{:header Headers :groups [Group] :totals Totals}

(def ReportData

)

(s/defn analyze-ad-performance :- [events :- params :- Params] (-> events (group-up params) summarize add-total-row (add-headers params)))

Page 35: Contracts in-clojure-pete

ReportData(s/defn analyze-ad-performance :- [events :- params :- Params] (-> events (group-up params) summarize add-total-row (add-headers params)))

[Event]

Page 36: Contracts in-clojure-pete

(deftest analyze-ad-performance-test (testing "grouping of rows" (let [… result (analyze-ad-performance events {}) (is (= expected (:groups result))))))

(s/defn analyze-ad-performance :-

params :- Params]

(group-up params)

add-total-row (add-headers params)))

Page 37: Contracts in-clojure-pete

(deftest analyze-ad-performance-test (testing "grouping of rows" (let [… result (analyze-ad-performance events {}) (is (= expected (:groups result))))))

(use-fixtures schema.test/validate-schemas)

Page 38: Contracts in-clojure-pete

(let [… result (analyze-ad-performance events {}) (is (= expected (:groups result))))))

Input does not match schema Params

Missing required key :title Missing required key :start Missing required key :end

Page 39: Contracts in-clojure-pete

:title string:start date:end date

Params

Page 40: Contracts in-clojure-pete

:title string:start date:end date

:title string:start date:end date

param-gen

Page 41: Contracts in-clojure-pete

:title string that isn't empty:start date before end date:end date before now

:title string:start date:end date

:title string:start date:end date

param-gen

Page 42: Contracts in-clojure-pete

:title string that isn't empty:start date before end date:end date before now

:title string:start date:end date

param-gen

Page 43: Contracts in-clojure-pete

(deftest analyze-ad-performance-test (testing "grouping of rows" (let [… result (analyze-ad-performance [events] (sample-one param-gen)) (is (= expected (:groups result))))))

Page 44: Contracts in-clojure-pete

(defspec analyze-ad-performance-spec 100 (for-all [events events-gen params param-gen] (analyze-ad-performance events params))))

Page 45: Contracts in-clojure-pete

Q: What do we know?

A: Schemas

Q: How do we know it?

A: Generative Tests

Page 46: Contracts in-clojure-pete

Q: What do we know?

data shape

Page 47: Contracts in-clojure-pete

We live in this weird time where a rose by any other name throws a compile/runtime error. @deech

Page 48: Contracts in-clojure-pete

Q: What do we know?

data shape

data value boundaries

Page 49: Contracts in-clojure-pete

:title - string - not empty - capitalized

Headers

Page 50: Contracts in-clojure-pete

(def Headers {:title (s/both s/Str (s/pred (complement empty?) "nonempty") (s/pred capitalized? "Title Caps")) …})

Page 51: Contracts in-clojure-pete

Q: What do we know?

data shape

value boundaries

relationships within values

Page 52: Contracts in-clojure-pete

{:title … :start DateTime :end DateTime} - start is before end

Headers

Page 53: Contracts in-clojure-pete

Q: What could we know?

produced types

Page 54: Contracts in-clojure-pete

(s/def params :- (generator-of Params) (gen/hash-map :title … :start … :end …))

what

if?

Page 55: Contracts in-clojure-pete

(defgen params Params (gen/hash-map :title … :start … :end …))

what

if?

Page 56: Contracts in-clojure-pete

(defgen params Params (gen/hash-map :title … :start … :end …))

what

if?

Generator: my-project.generators/params Value: {:end #<DateTime -58693684-08-30T03:25:35.104Z>, :start #<DateTime -71419883-06-14T11:43:44.640Z>, :title ""} Error: (not (keyword? a-clojure.lang.PersistentArrayMap))

Page 57: Contracts in-clojure-pete

(s/defn fetch-events :- [Event] [params] …)

Page 58: Contracts in-clojure-pete

(s/defn fetch-events :- (lazy-seq-of Event) [params] …)

what

if?

Page 59: Contracts in-clojure-pete

Generator of A

Function from A to B

Lazy sequence of A

Page 60: Contracts in-clojure-pete

Q: What could we know?

produced types

relationships between types

Page 61: Contracts in-clojure-pete

(s/defn sample-one [A :- Schema] (s/fn :- A [g :- (generator-of A)] (last (gen/sample g))))

((sample-one Params) params-gen)

Page 62: Contracts in-clojure-pete

(tdefn sample-one :- A [A :- Schema g :- (generator-of A)] (last (gen/sample g)))

what

if?

(sample-one Params param-gen)

(sample-one param-gen)

Page 63: Contracts in-clojure-pete

what

if?(tdefn add-headers :- (merge A {:header Headers}) [data-so-far :- [A :< AnyMap] params])

Page 64: Contracts in-clojure-pete

what

if?

(add-headers data params)

(add-headers GroupsWithTotal data params)

(tdefn add-headers :- (merge A {:header Headers}) [data-so-far :- [A :< AnyMap] params])

Page 65: Contracts in-clojure-pete

Q: What could we know?

produced types

relationships between types

relationships between values

Page 66: Contracts in-clojure-pete

(defn group-up [events params] {:post [(as-lazy-as events %)] …))

Page 67: Contracts in-clojure-pete

relationships between types

produced types

relationships between values

data shape

data value boundaries

relationships within values

Page 68: Contracts in-clojure-pete
Page 69: Contracts in-clojure-pete
Page 70: Contracts in-clojure-pete

(def Event {:when DateTime :what Incident :who Customer})

(s/defn fetch-events :- [Event] [params] …)

Page 71: Contracts in-clojure-pete

(def Event {:when DateTime :what Incident :who Customer})

(s/defn fetch-events :- [t/Event] [params] …)

(:require [my-project.schemas :as t])

my-project.schemas

Page 72: Contracts in-clojure-pete

implementation

schemas

Page 73: Contracts in-clojure-pete

implementation

schemas

src

Page 74: Contracts in-clojure-pete

(def params (hash-map :title gen/string-alpha-numeric :start (datetime-before (now)) :end (datetime-before (now))))

(defspec analyze-ad-performance-spec 100 (for-all [events mygen/events params mygen/params] (analyze-ad-performance events params))))

(:require [my-project.gen :as mygen])

my-project.gen

Page 75: Contracts in-clojure-pete

tests

generators

Page 76: Contracts in-clojure-pete

test

tests

generators

Page 77: Contracts in-clojure-pete

test

tests

generators

Page 78: Contracts in-clojure-pete

test

tests

generators

implementation

schemas

src

Page 79: Contracts in-clojure-pete

test

tests

generators

implementation

schemas

src

Page 80: Contracts in-clojure-pete

test

tests

generators

implementation

schemas

src

src testclient

Page 81: Contracts in-clojure-pete

test

tests

generators

implementation

schemas

src

src testclient

Page 82: Contracts in-clojure-pete

implementation

schemas generators

tests

Page 83: Contracts in-clojure-pete

implementation

schemas generators

tests

Page 84: Contracts in-clojure-pete

implementation

schemas

generators

tests

Page 85: Contracts in-clojure-pete

implementation

schemas

generators

tests

Page 86: Contracts in-clojure-pete

implementation

schemas

generators

tests

src testclient

Page 87: Contracts in-clojure-pete

implementation

schemas generators

tests

src test

Page 88: Contracts in-clojure-pete

implementation

schemas

generators

tests

Page 89: Contracts in-clojure-pete

implementation

schemas

generators

tests

src testclient

Page 90: Contracts in-clojure-pete

testkitimplementation

schemas

generators

tests

Page 91: Contracts in-clojure-pete

testkitimplementation

schemas

generators

tests

Page 92: Contracts in-clojure-pete

testkitimplementation

schemas

generators

tests

Page 93: Contracts in-clojure-pete

testkitimplementation

schemas

generators

tests

Page 94: Contracts in-clojure-pete
Page 95: Contracts in-clojure-pete

Executable Specifications

what do we know?

how do we know it?

Page 96: Contracts in-clojure-pete
Page 97: Contracts in-clojure-pete

Science!

test.check

prismatic/schema

Clojure

Page 98: Contracts in-clojure-pete

Science!

generative tests

types and contracts

… your language …

Science!

Page 99: Contracts in-clojure-pete

Science!

in-memory test tools

native API definitions

… your language …

everyone!forScience!

Page 100: Contracts in-clojure-pete

Formal Proofs

Informal Reasoning

Experimental Evidence

Page 101: Contracts in-clojure-pete

@jessitronblog.jessitron.com

https://github.com/jessitron/contracts-as-types-exampleshttps://github.com/jessitron/slack-client

examples

resourceshttps://github.com/Prismatic/schemahttps://github.com/miner/herbert (value relationships)http://david-mcneil.com/post/114783282473/extending-prismatic-schema-to-higher-order

https://github.com/jessitron/schematron

http://dl.acm.org/citation.cfm?id=2661156Static typing and productivity: Stefik & Hanenberg 2014