Skip to content

Instantly share code, notes, and snippets.

@Vinai
Last active July 22, 2019 13:48
Show Gist options
  • Save Vinai/a2f9b8b3d42b08c4e4c69e7b1685d48e to your computer and use it in GitHub Desktop.
Save Vinai/a2f9b8b3d42b08c4e4c69e7b1685d48e to your computer and use it in GitHub Desktop.
Example of property based tests for a simple algorithm in Clojure and in PHP.
(ns example.core
(:require [clojure.test :refer :all]
[clojure.test.check :as tc]
[clojure.test.check.generators :as gen]
[clojure.test.check.properties :as prop]))
;; -----------------------------------------------------------------------
;; Application code to test
(defn includes? [needle haystack]
(some #{needle} haystack))
;; function to be tested
(defn pick-create-list-strategy [create-lists]
(cond
(includes? "AUTO" create-lists) "AUTO"
(includes? "MANUAL" create-lists) "MANUAL"
:else "NONE"))
;; -----------------------------------------------------------------------
;; Generators
;; generator for a vec of random strings (except AUTO or MANUAL)
(def generate-input-without-manual-or-auto
(gen/such-that #(not (or (includes? "AUTO" %) (includes? "MANUAL" %))) (gen/vector gen/string)))
;; generator for a vec of random strings that always also includes MANUAL
(def generate-input-with-manual
(gen/bind generate-input-without-manual-or-auto
(fn [others]
(gen/shuffle (conj others "MANUAL")))))
;; generator for a vec of random strings that always includes AUTO and might also include MANUAL
(def generate-input-with-auto
(gen/bind (gen/one-of [generate-input-without-manual-or-auto generate-input-with-manual])
(fn [others]
(gen/shuffle (conj others "AUTO")))))
;; -----------------------------------------------------------------------
;; Tests
(deftest test-list-creation-strategy-selection
(testing "If no AUTO or MANUAL is present then NONE is returned"
(let [property (prop/for-all [input generate-input-without-manual-or-auto]
(= "NONE" (pick-create-list-strategy input)))]
(is (:result (tc/quick-check 500 property)))))
(testing "If MANUAL is present but no AUTO then MANUAL is returned"
(let [property (prop/for-all [input generate-input-with-manual]
(= "MANUAL" (pick-create-list-strategy input)))]
(is (:result (tc/quick-check 500 property)))))
(testing "If AUTO is present AUTO is returned, even if MANUAL is also present"
(let [property (prop/for-all [input generate-input-with-auto]
(= "AUTO" (pick-create-list-strategy input)))]
(is (:result (tc/quick-check 500 property))))))
<?php
use PHPUnit\Framework\TestCase;
use Eris\Generator;
use function array_map as map;
/* Function under test */
function pickCreateListStrategy(array $createListings)
{
$hasAuto = in_array('AUTO', $createListings, true);
$hasManual = in_array('MANUAL', $createListings, true);
return $hasAuto ? 'AUTO' : ($hasManual ? 'MANUAL' : 'NONE');
}
class Example extends TestCase
{
use \Eris\TestTrait;
/**
* @before
*/
public function configureEris()
{
$this->limitTo(500);
}
/* Generators */
private function generateInputWithoutManualOrAuto(): Generator {
return Generator\suchThat(
function (array $input): bool {
return !(in_array('AUTO', $input, true) || in_array('MANUAL', $input, true));
},
Generator\seq(Generator\string())
);
}
private function generateInputWithManual(): Generator
{
return Generator\bind(
$this->generateInputWithoutManualOrAuto(),
function (array $others) {
$others[] = 'MANUAL';
shuffle($others);
return Generator\constant($others);
}
);
}
private function generateInputWithAuto(): Generator
{
return Generator\bind(
Generator\oneOf($this->generateInputWithoutManualOrAuto(), $this->generateInputWithManual()),
function (array $others) {
$others[] = 'AUTO';
shuffle($others);
return Generator\constant($others);
}
);
}
/* Tests */
public function testIfNoAutoOrManualIsPresent_NoneIsReturned()
{
$property = $this->forAll($this->generateInputWithoutManualOrAuto());
$property(function (array $input): void {
$this->assertSame('NONE', pickCreateListStrategy($input));
});
}
public function testIfManualIsPresent_ManualIsReturned()
{
$property = $this->forAll($this->generateInputWithManual());
$property(function (array $input): void {
$this->assertSame('MANUAL', pickCreateListStrategy($input));
});
}
public function testIfAutoIsPresent_AutoIsReturned()
{
$property = $this->forAll($this->generateInputWithAuto());
$property(function (array $input): void {
$this->assertSame('AUTO', pickCreateListStrategy($input));
});
}
}
@Vinai
Copy link
Author

Vinai commented Jul 20, 2019

Note: Clojure 1.10 runs the 1500 tests in total in 0.8s, PHP 7.2 takes 57s on my MBP.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment