All Projects → sgepigon → piggy

sgepigon / piggy

Licence: EPL-1.0 license
Test for spec compatibility and breaking changes.

Programming Languages

clojure
4091 projects

Projects that are alternatives of or similar to piggy

kitimat
A library for generative, property-based testing in TypeScript and Jest.
Stars: ✭ 68 (+51.11%)
Mutual labels:  property-based-testing, generative-testing
Fast Check
Property based testing framework for JavaScript (like QuickCheck) written in TypeScript
Stars: ✭ 2,604 (+5686.67%)
Mutual labels:  property-based-testing, generative-testing
clausejs
Write contract once. Get data & function validators & conformers, an accurate & readable project contract, auto-generated API documentation, generative test coverage, plus more. A tool that enables a more predictable workflow for developing your JavaScript projects.
Stars: ✭ 29 (-35.56%)
Mutual labels:  property-based-testing, clojure-spec
ava-fast-check
Property based testing for AVA based on fast-check
Stars: ✭ 44 (-2.22%)
Mutual labels:  property-based-testing, generative-testing
hypothesis-gufunc
Extension to hypothesis for testing numpy general universal functions
Stars: ✭ 32 (-28.89%)
Mutual labels:  property-based-testing, generative-testing
Elixir Type check
TypeCheck: Fast and flexible runtime type-checking for your Elixir projects.
Stars: ✭ 80 (+77.78%)
Mutual labels:  property-based-testing
Deal
Design by contract for Python with static checker and tests' generation.
Stars: ✭ 164 (+264.44%)
Mutual labels:  property-based-testing
Qcstm
A simple state-machine framework for OCaml based on QCheck
Stars: ✭ 50 (+11.11%)
Mutual labels:  property-based-testing
Junit Quickcheck
Property-based testing, JUnit-style
Stars: ✭ 821 (+1724.44%)
Mutual labels:  property-based-testing
Fuzzcheck Rs
Structure-aware, in-process, coverage-guided, evolutionary fuzzing engine for Rust functions.
Stars: ✭ 247 (+448.89%)
Mutual labels:  property-based-testing
Rapid
Rapid is a Go library for property-based testing that supports state machine ("stateful" or "model-based") testing and fully automatic test case minimization ("shrinking")
Stars: ✭ 213 (+373.33%)
Mutual labels:  property-based-testing
Expect More
Curried Type Testing library, and Test Matchers for Jest
Stars: ✭ 124 (+175.56%)
Mutual labels:  property-based-testing
Swiftcheck
QuickCheck for Swift
Stars: ✭ 1,319 (+2831.11%)
Mutual labels:  property-based-testing
Spec
Data specification conformance and generation for Elixir
Stars: ✭ 72 (+60%)
Mutual labels:  property-based-testing
Fsharp Hedgehog
Release with confidence, state-of-the-art property testing for .NET.
Stars: ✭ 219 (+386.67%)
Mutual labels:  property-based-testing
Quick check.js
A JS implementation of quick_check
Stars: ✭ 48 (+6.67%)
Mutual labels:  property-based-testing
Smallcheck
Test your Haskell code by exhaustively checking its properties
Stars: ✭ 114 (+153.33%)
Mutual labels:  property-based-testing
Qcheck
QuickCheck inspired property-based testing for OCaml.
Stars: ✭ 194 (+331.11%)
Mutual labels:  property-based-testing
Hypothesis Jsonschema
Tools to generate test data from JSON schemata with Hypothesis
Stars: ✭ 112 (+148.89%)
Mutual labels:  property-based-testing
Functionaljava
Functional programming in Java
Stars: ✭ 1,472 (+3171.11%)
Mutual labels:  property-based-testing

piggy

“Ralph made a step forward and Jack smacked Piggy’s head. Piggy’s glasses flew off and tinkled on the rocks. Piggy cried out in terror: ‘My specs! One side’s broken.”

— William Golding, Lord of the Flies, Chapter 4

piggy is a Clojure library that helps with broken specs.

Project Status

Will be in alpha as long as clojure.spec is in alpha.

Installation

Download from https://github.com/sgepigon/piggy.

Usage

Let's say we're writing a spec for clojure.core/+. We might start out like this:

(ns piggy.demo.readme
  (:require
   [clojure.spec.alpha :as s]
   [piggy.combinators.alpha :as pc]))

(+ 2 2)
;; => 4

(s/fspec :args (s/cat :x int? :y int?)
         :ret int?)

;; `s/fspec` returns the function itself on a successful conform
(s/conform (s/fspec :args (s/cat :x int? :y int?)
                    :ret int?)
           +)
;; => #function[clojure.core/+]

;; Can also use `s/explain` to print a human readable message:
(s/explain (s/fspec :args (s/cat :x int? :y int?)
                    :ret int?)
           +)
;; => Success!

We think about it a little more and realize + is variadic:

(+) ; => 0
(+ 1) ; => 1
(+ 0 1 2 3 4 5 6 7 8 9) ; => 45

We can use s/* for zero or more ints.

(s/explain (s/fspec :args (s/* int?)
                    :ret int?)
           +)
;; => (-8782045379102980082 -441326657751795727) - failed: integer overflow

Two things are going on here:

  1. Because we switch from a 2-arity—(s/cat :x int? y: int?)—to variadic—(s/* int?)—we're hitting integer overflow.

  2. Turns out clojure.core/+ doesn't auto-promote. But there is a built-in Clojure function, clojure.core/+', that does arbitrary precision.

    So let's try this with +'.

(s/explain (s/fspec :args (s/* int?)
                    :ret int?)
           +')
;; => 9223372036854775808N - failed: int? at: [:ret]

We can see it auto-promotes but now our return spec is wrong. We also realize +' takes all sorts of numbers.

(+ -1 3.14 22/7 0x77)
;; => 124.28285714285714

Let's update int? to number?

(s/explain (s/fspec :args (s/* number?)
                    :ret number?)
           +')
;; => Success!

Cool, we're more comfortable with this spec. How do we know change isn't a breaking change?

compat and fcompat are spec combinators that encodes a compatibility property between two specs.

compat is for simple comparsions over specs and predicates while fcompat is used to compare compatibility between functions. s/spec is to compat as s/fspec is to fcompat.

The combinators are is a spec themselves, so the same functions that work on clojure.spec specs (s/conform, s/unform, s/explain, s/gen, s/with-gen, and s/describe) work on compat and fcompat.

(s/explain (pc/fcompat :old (s/fspec :args (s/cat :x int? :y int?)
                                     :ret int?)
                       :new (s/fspec :args (s/* number?)
                                     :ret number?))
           +')
;; => 1.0 - failed: int? at: [:ret :old]

We see here it's a breaking change: we promised an int? but now we're saying we're returning a number?—this is weakening a promise.

We can show it trivially with clojure.core/any?

(s/explain (pc/fcompat :old (s/fspec :args (s/* number?)
                                     :ret number?)
                       :new (s/fspec :args (s/* number?)
                                     :ret any?))
           +')
;; => \space - failed: clojure.core/number?: any? is less constrained than
;; number? at: [:ret :old]

Similarlity, we can show a breaking change by requiring more in the arguments:

(s/explain (pc/fcompat :old (s/fspec :args (s/* number?)
                                     :ret number?)
                       :new (s/fspec :args (s/cat :x number? :y number?)
                                     :ret number?))
           +')
;; => () - failed: Insufficient input at: [:args :new :x]

Developers

Run Clojure unit tests with either:

lein test

or

clj -A:test

License

Copyright © 2018–2019 Santiago Gepigon III

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].