Skip to content

Instantly share code, notes, and snippets.

@hchasestevens
Created March 5, 2018 21:58
Show Gist options
  • Save hchasestevens/2723f6ae06b09e995a52fcc42d6c05e9 to your computer and use it in GitHub Desktop.
Save hchasestevens/2723f6ae06b09e995a52fcc42d6c05e9 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Property-based testing"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"from hypothesis import given, assume, example, note, settings, strategies as st\n",
"from hypothesis.stateful import RuleBasedStateMachine, rule, invariant\n",
"from random import randint\n",
"from collections import Counter\n",
"import unittest"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Motivation"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Would you accept `plus`?"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"def plus(x, y):\n",
" if x and y:\n",
" return 2\n",
" return 1"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"assert plus(1, 0) == 1\n",
"assert plus(0, 1) == 1\n",
"assert plus(1, 1) == 2"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"def plus(x, y):\n",
" if x and y:\n",
" return 2\n",
" return 1\n"
]
}
],
"source": [
"%history 2"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"def plus_v2(a, b):\n",
" return a + b"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Would you accept `plus_v2`?"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"for _ in range(1000):\n",
" a = randint(-100, 100)\n",
" b = randint(-100, 100)\n",
" assert plus_v2(a, b) == a + b"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"def plus_v2(a, b):\n",
" return a + b\n"
]
}
],
"source": [
"%history 3"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Can we test `plus_v2` without `+`?"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"def associative(a, b):\n",
" return plus_v2(plus_v2(a, b), 1) == plus_v2(a, plus_v2(b, 1))"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"def commutative(a, b):\n",
" return plus_v2(a, b) == plus_v2(b, a)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"def zero_is_identity(a):\n",
" return plus_v2(a, 0) == a"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"for _ in range(1000):\n",
" a = randint(-100, 100)\n",
" b = randint(-100, 100)\n",
" assert associative(a, b)\n",
" assert commutative(a, b)\n",
" assert zero_is_identity(a)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Rewriting in Hypothesis"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"@given(a=st.integers(), b=st.integers())\n",
"def test_plus_v2(a, b):\n",
" assert associative(a, b)\n",
" assert commutative(a, b)\n",
" assert zero_is_identity(a)\n",
" \n",
"test_plus_v2()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Generating values"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"-30412"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"st.integers().example()"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"-1e-05"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"st.floats().example()"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"u'\\U0005423a'"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"st.text().example()"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[False]"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"st.lists(st.booleans()).example()"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"['Context',\n",
" 'Decimal',\n",
" 'FloatKey',\n",
" 'Fraction',\n",
" 'InvalidArgument',\n",
" 'LRUReusedCache',\n",
" 'NOTHING',\n",
" 'Nothing',\n",
" 'RandomSeeder',\n",
" 'ResolutionFailed',\n",
" 'STRATEGY_CACHE',\n",
" 'SearchStrategy',\n",
" '_AVERAGE_LIST_LENGTH',\n",
" '__all__',\n",
" '__builtins__',\n",
" '__doc__',\n",
" '__file__',\n",
" '__name__',\n",
" '__package__',\n",
" '_defer_from_type',\n",
" '_strategies',\n",
" 'absolute_import',\n",
" 'assume',\n",
" 'base_defines_strategy',\n",
" 'binary',\n",
" 'booleans',\n",
" 'builds',\n",
" 'cacheable',\n",
" 'ceil',\n",
" 'characters',\n",
" 'check_strategy',\n",
" 'check_type',\n",
" 'check_valid_bound',\n",
" 'check_valid_integer',\n",
" 'check_valid_interval',\n",
" 'check_valid_sizes',\n",
" 'choices',\n",
" 'complex_numbers',\n",
" 'composite',\n",
" 'convert_value',\n",
" 'count_between_floats',\n",
" 'data',\n",
" 'dates',\n",
" 'datetimes',\n",
" 'decimals',\n",
" 'deferred',\n",
" 'defines_strategy',\n",
" 'defines_strategy_with_reusable_values',\n",
" 'dictionaries',\n",
" 'division',\n",
" 'dt',\n",
" 'enum',\n",
" 'fixed_dictionaries',\n",
" 'float_to_int',\n",
" 'floats',\n",
" 'floor',\n",
" 'fractions',\n",
" 'from_regex',\n",
" 'from_type',\n",
" 'frozensets',\n",
" 'gcd',\n",
" 'get_type_hints',\n",
" 'getfullargspec',\n",
" 'hrange',\n",
" 'implements_iterator',\n",
" 'infer',\n",
" 'int_to_float',\n",
" 'integers',\n",
" 'is_negative',\n",
" 'isclass',\n",
" 'isfunction',\n",
" 'iterables',\n",
" 'just',\n",
" 'lists',\n",
" 'math',\n",
" 'none',\n",
" 'not_set',\n",
" 'note_deprecation',\n",
" 'nothing',\n",
" 'one_of',\n",
" 'operator',\n",
" 'permutations',\n",
" 'print_function',\n",
" 'proxies',\n",
" 'random_module',\n",
" 'randoms',\n",
" 'recursive',\n",
" 'reduce',\n",
" 'register_type_strategy',\n",
" 'renamed_arguments',\n",
" 'required_args',\n",
" 'runner',\n",
" 'sampled_from',\n",
" 'sets',\n",
" 'shared',\n",
" 'streaming',\n",
" 'text',\n",
" 'text_type',\n",
" 'timedeltas',\n",
" 'times',\n",
" 'try_convert',\n",
" 'tuples',\n",
" 'uuids']"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dir(st)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Property-based testing patterns\n",
"\n",
"* \"Doesn't blow up\"\n",
"* Encode/decode\n",
"* Idempotence\n",
"* Oracle"
]
}
],
"metadata": {
"celltoolbar": "Slideshow",
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.14"
}
},
"nbformat": 4,
"nbformat_minor": 1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment