{"id":84563,"date":"2026-04-19T15:19:17","date_gmt":"2026-04-19T15:19:17","guid":{"rendered":"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/"},"modified":"2026-04-19T15:19:17","modified_gmt":"2026-04-19T15:19:17","slug":"a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design","status":"publish","type":"post","link":"https:\/\/youzum.net\/th\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/","title":{"rendered":"A Coding Guide for Property-Based Testing Using Hypothesis with Stateful, Differential, and Metamorphic Test Design"},"content":{"rendered":"<p>In this tutorial, we explore property-based testing using <a href=\"https:\/\/github.com\/HypothesisWorks\/hypothesis\"><strong>Hypothesis<\/strong><\/a> and build a rigorous testing pipeline that goes far beyond traditional unit testing. We implement invariants, differential testing, metamorphic testing, targeted exploration, and stateful testing to validate both functional correctness and behavioral guarantees of our systems. Instead of manually crafting edge cases, we let Hypothesis generate structured inputs, shrink failures to minimal counterexamples, and systematically uncover hidden bugs. Also, we demonstrate how modern testing practices can be integrated directly into experimental and research-driven workflows.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\"no-line-numbers\"><code class=\"no-wrap language-php\">import sys, textwrap, subprocess, os, re, math\n!{sys.executable} -m pip -q install hypothesis pytest\n\n\ntest_code = r'''\nimport re, math\nimport pytest\nfrom hypothesis import (\n   given, assume, example, settings, note, target,\n   HealthCheck, Phase\n)\nfrom hypothesis import strategies as st\nfrom hypothesis.stateful import RuleBasedStateMachine, rule, invariant, initialize, precondition\n\n\ndef clamp(x: int, lo: int, hi: int) -&gt; int:\n   if x &lt; lo:\n       return lo\n   if x &gt; hi:\n       return hi\n   return x\n\n\ndef normalize_whitespace(s: str) -&gt; str:\n   return \" \".join(s.split())\n\n\ndef is_sorted_non_decreasing(xs):\n   return all(xs[i] &lt;= xs[i+1] for i in range(len(xs)-1))\n\n\ndef merge_sorted(a, b):\n   i = j = 0\n   out = []\n   while i &lt; len(a) and j &lt; len(b):\n       if a[i] &lt;= b[j]:\n           out.append(a[i]); i += 1\n       else:\n           out.append(b[j]); j += 1\n   out.extend(a[i:])\n   out.extend(b[j:])\n   return out\n\n\ndef merge_sorted_reference(a, b):\n   return sorted(list(a) + list(b))\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We set up the environment by installing Hypothesis and pytest and importing all required modules. We begin constructing the full test suite by defining core utility functions such as clamp, normalize_whitespace, and merge_sorted. We establish the functional foundation that our property-based tests will rigorously validate in later snippets.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\"no-line-numbers\"><code class=\"no-wrap language-php\">def safe_parse_int(s: str):\n   t = s.strip()\n   if re.fullmatch(r\"[+-]?d+\", t) is None:\n       return (False, \"not_an_int\")\n   if len(t.lstrip(\"+-\")) &gt; 2000:\n       return (False, \"too_big\")\n   try:\n       return (True, int(t))\n   except Exception:\n       return (False, \"parse_error\")\n\n\ndef safe_parse_int_alt(s: str):\n   t = s.strip()\n   if not t:\n       return (False, \"not_an_int\")\n   sign = 1\n   if t[0] == \"+\":\n       t = t[1:]\n   elif t[0] == \"-\":\n       sign = -1\n       t = t[1:]\n   if not t or any(ch &lt; \"0\" or ch &gt; \"9\" for ch in t):\n       return (False, \"not_an_int\")\n   if len(t) &gt; 2000:\n       return (False, \"too_big\")\n   val = 0\n   for ch in t:\n       val = val * 10 + (ord(ch) - 48)\n   return (True, sign * val)\n\n\nbounds = st.tuples(st.integers(-10_000, 10_000), st.integers(-10_000, 10_000)).map(\n   lambda t: (t[0], t[1]) if t[0] &lt;= t[1] else (t[1], t[0])\n)\n\n\n@st.composite\ndef int_like_strings(draw):\n   sign = draw(st.sampled_from([\"\", \"+\", \"-\"]))\n   digits = draw(st.text(alphabet=st.characters(min_codepoint=48, max_codepoint=57), min_size=1, max_size=300))\n   left_ws = draw(st.text(alphabet=[\" \", \"t\", \"n\"], min_size=0, max_size=5))\n   right_ws = draw(st.text(alphabet=[\" \", \"t\", \"n\"], min_size=0, max_size=5))\n   return f\"{left_ws}{sign}{digits}{right_ws}\"\n\n\nsorted_lists = st.lists(st.integers(-10_000, 10_000), min_size=0, max_size=200).map(sorted)<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We implement parsing logic and define structured strategies that generate constrained, meaningful test inputs. We create composite strategies such as int_like_strings to precisely control the input space for property validation. We prepare sorted list generators and bounds strategies that enable differential and invariant-based testing.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\"no-line-numbers\"><code class=\"no-wrap language-php\">@settings(max_examples=300, suppress_health_check=[HealthCheck.too_slow])\n@given(x=st.integers(-50_000, 50_000), b=bounds)\ndef test_clamp_within_bounds(x, b):\n   lo, hi = b\n   y = clamp(x, lo, hi)\n   assert lo &lt;= y &lt;= hi\n\n\n@settings(max_examples=300, suppress_health_check=[HealthCheck.too_slow])\n@given(x=st.integers(-50_000, 50_000), b=bounds)\ndef test_clamp_idempotent(x, b):\n   lo, hi = b\n   y = clamp(x, lo, hi)\n   assert clamp(y, lo, hi) == y\n\n\n@settings(max_examples=250)\n@given(s=st.text())\n@example(\"   attb n c  \")\ndef test_normalize_whitespace_is_idempotent(s):\n   t = normalize_whitespace(s)\n   assert normalize_whitespace(t) == t\n   assert normalize_whitespace(\" nt \" + s + \"  t\") == normalize_whitespace(s)\n\n\n@settings(max_examples=250, suppress_health_check=[HealthCheck.too_slow])\n@given(a=sorted_lists, b=sorted_lists)\ndef test_merge_sorted_matches_reference(a, b):\n   out = merge_sorted(a, b)\n   ref = merge_sorted_reference(a, b)\n   assert out == ref\n   assert is_sorted_non_decreasing(out)<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We define core property tests that validate correctness and idempotence across multiple functions. We use Hypothesis decorators to automatically explore edge cases and verify behavioral guarantees such as boundary constraints and deterministic normalization. We also implement differential testing to ensure our merge implementation matches a trusted reference.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\"no-line-numbers\"><code class=\"no-wrap language-php\">@settings(max_examples=250, deadline=200, suppress_health_check=[HealthCheck.too_slow])\n@given(s=int_like_strings())\ndef test_two_parsers_agree_on_int_like_strings(s):\n   ok1, v1 = safe_parse_int(s)\n   ok2, v2 = safe_parse_int_alt(s)\n   assert ok1 and ok2\n   assert v1 == v2\n\n\n@settings(max_examples=250)\n@given(s=st.text(min_size=0, max_size=200))\ndef test_safe_parse_int_rejects_non_ints(s):\n   t = s.strip()\n   m = re.fullmatch(r\"[+-]?d+\", t)\n   ok, val = safe_parse_int(s)\n   if m is None:\n       assert ok is False\n   else:\n       if len(t.lstrip(\"+-\")) &gt; 2000:\n           assert ok is False and val == \"too_big\"\n       else:\n           assert ok is True and isinstance(val, int)\n\n\ndef variance(xs):\n   if len(xs) &lt; 2:\n       return 0.0\n   mu = sum(xs) \/ len(xs)\n   return sum((x - mu) ** 2 for x in xs) \/ (len(xs) - 1)\n\n\n@settings(max_examples=250, phases=[Phase.generate, Phase.shrink])\n@given(xs=st.lists(st.integers(-1000, 1000), min_size=0, max_size=80))\ndef test_statistics_sanity(xs):\n   target(variance(xs))\n   if len(xs) == 0:\n       assert variance(xs) == 0.0\n   elif len(xs) == 1:\n       assert variance(xs) == 0.0\n   else:\n       v = variance(xs)\n       assert v &gt;= 0.0\n       k = 7\n       assert math.isclose(variance([x + k for x in xs]), v, rel_tol=1e-12, abs_tol=1e-12)<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We extend our validation to parsing robustness and statistical correctness using targeted exploration. We verify that two independent integer parsers agree on structured inputs and enforce rejection rules on invalid strings. We further implement metamorphic testing by validating invariants of variance under transformation.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\"no-line-numbers\"><code class=\"no-wrap language-php\">class Bank:\n   def __init__(self):\n       self.balance = 0\n       self.ledger = []\n\n\n   def deposit(self, amt: int):\n       if amt &lt;= 0:\n           raise ValueError(\"deposit must be positive\")\n       self.balance += amt\n       self.ledger.append((\"dep\", amt))\n\n\n   def withdraw(self, amt: int):\n       if amt &lt;= 0:\n           raise ValueError(\"withdraw must be positive\")\n       if amt &gt; self.balance:\n           raise ValueError(\"insufficient funds\")\n       self.balance -= amt\n       self.ledger.append((\"wd\", amt))\n\n\n   def replay_balance(self):\n       bal = 0\n       for typ, amt in self.ledger:\n           bal += amt if typ == \"dep\" else -amt\n       return bal\n\n\nclass BankMachine(RuleBasedStateMachine):\n   def __init__(self):\n       super().__init__()\n       self.bank = Bank()\n\n\n   @initialize()\n   def init(self):\n       assert self.bank.balance == 0\n       assert self.bank.replay_balance() == 0\n\n\n   @rule(amt=st.integers(min_value=1, max_value=10_000))\n   def deposit(self, amt):\n       self.bank.deposit(amt)\n\n\n   @precondition(lambda self: self.bank.balance &gt; 0)\n   @rule(amt=st.integers(min_value=1, max_value=10_000))\n   def withdraw(self, amt):\n       assume(amt &lt;= self.bank.balance)\n       self.bank.withdraw(amt)\n\n\n   @invariant()\n   def balance_never_negative(self):\n       assert self.bank.balance &gt;= 0\n\n\n   @invariant()\n   def ledger_replay_matches_balance(self):\n       assert self.bank.replay_balance() == self.bank.balance\n\n\nTestBankMachine = BankMachine.TestCase\n'''\n\n\npath = \"\/tmp\/test_hypothesis_advanced.py\"\nwith open(path, \"w\", encoding=\"utf-8\") as f:\n   f.write(test_code)\n\n\nprint(\"Hypothesis version:\", __import__(\"hypothesis\").__version__)\nprint(\"nRunning pytest on:\", path, \"n\")\n\n\nres = subprocess.run([sys.executable, \"-m\", \"pytest\", \"-q\", path], capture_output=True, text=True)\nprint(res.stdout)\nif res.returncode != 0:\n   print(res.stderr)\n\n\nif res.returncode == 0:\n   print(\"nAll Hypothesis tests passed.\")\nelif res.returncode == 5:\n   print(\"nPytest collected no tests.\")\nelse:\n   print(\"nSome tests failed.\")\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We implement a stateful system using Hypothesis\u2019s rule-based state machine to simulate a bank account. We define rules, preconditions, and invariants to guarantee balance consistency and ledger integrity under arbitrary operation sequences. We then execute the entire test suite via pytest, allowing Hypothesis to automatically discover counterexamples and verify system correctness.<\/p>\n<p>In conclusion, we built a comprehensive property-based testing framework that validates pure functions, parsing logic, statistical behavior, and even stateful systems with invariants. We leveraged Hypothesis\u2019s shrinking, targeted search, and state machine testing capabilities to move from example-based testing to behavior-driven verification. It allows us to reason about correctness at a higher level of abstraction while maintaining strong guarantees for edge cases and system consistency.<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n<p>Check out\u00a0the<strong> <a href=\"https:\/\/github.com\/Marktechpost\/AI-Agents-Projects-Tutorials\/blob\/main\/LLM%20Evaluation\/hypothesis_property_based_testing_Marktechpost.ipynb\" target=\"_blank\" rel=\"noreferrer noopener\">Full Coding Notebook here<\/a><\/strong>.<strong>\u00a0<\/strong>Also,\u00a0feel free to follow us on\u00a0<strong><a href=\"https:\/\/x.com\/intent\/follow?screen_name=marktechpost\" target=\"_blank\" rel=\"noreferrer noopener\"><mark>Twitter<\/mark><\/a><\/strong>\u00a0and don\u2019t forget to join our\u00a0<strong><a href=\"https:\/\/www.reddit.com\/r\/machinelearningnews\/\" target=\"_blank\" rel=\"noreferrer noopener\">130k+ ML SubReddit<\/a><\/strong>\u00a0and Subscribe to\u00a0<strong><a href=\"https:\/\/www.aidevsignals.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">our Newsletter<\/a><\/strong>. Wait! are you on telegram?\u00a0<strong><a href=\"https:\/\/t.me\/machinelearningresearchnews\" target=\"_blank\" rel=\"noreferrer noopener\">now you can join us on telegram as well.<\/a><\/strong><\/p>\n<p>Need to partner with us for promoting your GitHub Repo OR Hugging Face Page OR Product Release OR Webinar etc.?\u00a0<strong><a href=\"https:\/\/forms.gle\/MTNLpmJtsFA3VRVd9\" target=\"_blank\" rel=\"noreferrer noopener\"><mark>Connect with us<\/mark><\/a><\/strong><\/p>\n<p>The post <a href=\"https:\/\/www.marktechpost.com\/2026\/04\/18\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/\">A Coding Guide for Property-Based Testing Using Hypothesis with Stateful, Differential, and Metamorphic Test Design<\/a> appeared first on <a href=\"https:\/\/www.marktechpost.com\/\">MarkTechPost<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>In this tutorial, we explore property-based testing using Hypothesis and build a rigorous testing pipeline that goes far beyond traditional unit testing. We implement invariants, differential testing, metamorphic testing, targeted exploration, and stateful testing to validate both functional correctness and behavioral guarantees of our systems. Instead of manually crafting edge cases, we let Hypothesis generate structured inputs, shrink failures to minimal counterexamples, and systematically uncover hidden bugs. Also, we demonstrate how modern testing practices can be integrated directly into experimental and research-driven workflows. Copy CodeCopiedUse a different Browser import sys, textwrap, subprocess, os, re, math !{sys.executable} -m pip -q install hypothesis pytest test_code = r&#8221;&#8217; import re, math import pytest from hypothesis import ( given, assume, example, settings, note, target, HealthCheck, Phase ) from hypothesis import strategies as st from hypothesis.stateful import RuleBasedStateMachine, rule, invariant, initialize, precondition def clamp(x: int, lo: int, hi: int) -&gt; int: if x &lt; lo: return lo if x &gt; hi: return hi return x def normalize_whitespace(s: str) -&gt; str: return &#8221; &#8220;.join(s.split()) def is_sorted_non_decreasing(xs): return all(xs[i] &lt;= xs[i+1] for i in range(len(xs)-1)) def merge_sorted(a, b): i = j = 0 out = [] while i &lt; len(a) and j &lt; len(b): if a[i] &lt;= b[j]: out.append(a[i]); i += 1 else: out.append(b[j]); j += 1 out.extend(a[i:]) out.extend(b[j:]) return out def merge_sorted_reference(a, b): return sorted(list(a) + list(b)) We set up the environment by installing Hypothesis and pytest and importing all required modules. We begin constructing the full test suite by defining core utility functions such as clamp, normalize_whitespace, and merge_sorted. We establish the functional foundation that our property-based tests will rigorously validate in later snippets. Copy CodeCopiedUse a different Browser def safe_parse_int(s: str): t = s.strip() if re.fullmatch(r&#8221;[+-]?d+&#8221;, t) is None: return (False, &#8220;not_an_int&#8221;) if len(t.lstrip(&#8220;+-&#8220;)) &gt; 2000: return (False, &#8220;too_big&#8221;) try: return (True, int(t)) except Exception: return (False, &#8220;parse_error&#8221;) def safe_parse_int_alt(s: str): t = s.strip() if not t: return (False, &#8220;not_an_int&#8221;) sign = 1 if t[0] == &#8220;+&#8221;: t = t[1:] elif t[0] == &#8220;-&#8220;: sign = -1 t = t[1:] if not t or any(ch &lt; &#8220;0&#8221; or ch &gt; &#8220;9&#8221; for ch in t): return (False, &#8220;not_an_int&#8221;) if len(t) &gt; 2000: return (False, &#8220;too_big&#8221;) val = 0 for ch in t: val = val * 10 + (ord(ch) &#8211; 48) return (True, sign * val) bounds = st.tuples(st.integers(-10_000, 10_000), st.integers(-10_000, 10_000)).map( lambda t: (t[0], t[1]) if t[0] &lt;= t[1] else (t[1], t[0]) ) @st.composite def int_like_strings(draw): sign = draw(st.sampled_from([&#8220;&#8221;, &#8220;+&#8221;, &#8220;-&#8220;])) digits = draw(st.text(alphabet=st.characters(min_codepoint=48, max_codepoint=57), min_size=1, max_size=300)) left_ws = draw(st.text(alphabet=[&#8221; &#8220;, &#8220;t&#8221;, &#8220;n&#8221;], min_size=0, max_size=5)) right_ws = draw(st.text(alphabet=[&#8221; &#8220;, &#8220;t&#8221;, &#8220;n&#8221;], min_size=0, max_size=5)) return f&#8221;{left_ws}{sign}{digits}{right_ws}&#8221; sorted_lists = st.lists(st.integers(-10_000, 10_000), min_size=0, max_size=200).map(sorted) We implement parsing logic and define structured strategies that generate constrained, meaningful test inputs. We create composite strategies such as int_like_strings to precisely control the input space for property validation. We prepare sorted list generators and bounds strategies that enable differential and invariant-based testing. Copy CodeCopiedUse a different Browser @settings(max_examples=300, suppress_health_check=[HealthCheck.too_slow]) @given(x=st.integers(-50_000, 50_000), b=bounds) def test_clamp_within_bounds(x, b): lo, hi = b y = clamp(x, lo, hi) assert lo &lt;= y &lt;= hi @settings(max_examples=300, suppress_health_check=[HealthCheck.too_slow]) @given(x=st.integers(-50_000, 50_000), b=bounds) def test_clamp_idempotent(x, b): lo, hi = b y = clamp(x, lo, hi) assert clamp(y, lo, hi) == y @settings(max_examples=250) @given(s=st.text()) @example(&#8221; attb n c &#8220;) def test_normalize_whitespace_is_idempotent(s): t = normalize_whitespace(s) assert normalize_whitespace(t) == t assert normalize_whitespace(&#8221; nt &#8221; + s + &#8221; t&#8221;) == normalize_whitespace(s) @settings(max_examples=250, suppress_health_check=[HealthCheck.too_slow]) @given(a=sorted_lists, b=sorted_lists) def test_merge_sorted_matches_reference(a, b): out = merge_sorted(a, b) ref = merge_sorted_reference(a, b) assert out == ref assert is_sorted_non_decreasing(out) We define core property tests that validate correctness and idempotence across multiple functions. We use Hypothesis decorators to automatically explore edge cases and verify behavioral guarantees such as boundary constraints and deterministic normalization. We also implement differential testing to ensure our merge implementation matches a trusted reference. Copy CodeCopiedUse a different Browser @settings(max_examples=250, deadline=200, suppress_health_check=[HealthCheck.too_slow]) @given(s=int_like_strings()) def test_two_parsers_agree_on_int_like_strings(s): ok1, v1 = safe_parse_int(s) ok2, v2 = safe_parse_int_alt(s) assert ok1 and ok2 assert v1 == v2 @settings(max_examples=250) @given(s=st.text(min_size=0, max_size=200)) def test_safe_parse_int_rejects_non_ints(s): t = s.strip() m = re.fullmatch(r&#8221;[+-]?d+&#8221;, t) ok, val = safe_parse_int(s) if m is None: assert ok is False else: if len(t.lstrip(&#8220;+-&#8220;)) &gt; 2000: assert ok is False and val == &#8220;too_big&#8221; else: assert ok is True and isinstance(val, int) def variance(xs): if len(xs) &lt; 2: return 0.0 mu = sum(xs) \/ len(xs) return sum((x &#8211; mu) ** 2 for x in xs) \/ (len(xs) &#8211; 1) @settings(max_examples=250, phases=[Phase.generate, Phase.shrink]) @given(xs=st.lists(st.integers(-1000, 1000), min_size=0, max_size=80)) def test_statistics_sanity(xs): target(variance(xs)) if len(xs) == 0: assert variance(xs) == 0.0 elif len(xs) == 1: assert variance(xs) == 0.0 else: v = variance(xs) assert v &gt;= 0.0 k = 7 assert math.isclose(variance([x + k for x in xs]), v, rel_tol=1e-12, abs_tol=1e-12) We extend our validation to parsing robustness and statistical correctness using targeted exploration. We verify that two independent integer parsers agree on structured inputs and enforce rejection rules on invalid strings. We further implement metamorphic testing by validating invariants of variance under transformation. Copy CodeCopiedUse a different Browser class Bank: def __init__(self): self.balance = 0 self.ledger = [] def deposit(self, amt: int): if amt &lt;= 0: raise ValueError(&#8220;deposit must be positive&#8221;) self.balance += amt self.ledger.append((&#8220;dep&#8221;, amt)) def withdraw(self, amt: int): if amt &lt;= 0: raise ValueError(&#8220;withdraw must be positive&#8221;) if amt &gt; self.balance: raise ValueError(&#8220;insufficient funds&#8221;) self.balance -= amt self.ledger.append((&#8220;wd&#8221;, amt)) def replay_balance(self): bal = 0 for typ, amt in self.ledger: bal += amt if typ == &#8220;dep&#8221; else -amt return bal class BankMachine(RuleBasedStateMachine): def __init__(self): super().__init__() self.bank = Bank() @initialize() def init(self): assert self.bank.balance == 0 assert self.bank.replay_balance() == 0 @rule(amt=st.integers(min_value=1, max_value=10_000)) def deposit(self, amt): self.bank.deposit(amt) @precondition(lambda self: self.bank.balance &gt; 0) @rule(amt=st.integers(min_value=1, max_value=10_000)) def withdraw(self, amt): assume(amt &lt;= self.bank.balance) self.bank.withdraw(amt) @invariant() def balance_never_negative(self): assert self.bank.balance &gt;= 0 @invariant() def ledger_replay_matches_balance(self): assert self.bank.replay_balance() == self.bank.balance TestBankMachine = BankMachine.TestCase &#8221;&#8217; path = &#8220;\/tmp\/test_hypothesis_advanced.py&#8221; with open(path, &#8220;w&#8221;, encoding=&#8221;utf-8&#8243;) as f: f.write(test_code) print(&#8220;Hypothesis version:&#8221;, __import__(&#8220;hypothesis&#8221;).__version__) print(&#8220;nRunning pytest on:&#8221;, path, &#8220;n&#8221;) res = subprocess.run([sys.executable, &#8220;-m&#8221;, &#8220;pytest&#8221;, &#8220;-q&#8221;, path], capture_output=True, text=True) print(res.stdout) if res.returncode != 0: print(res.stderr) if res.returncode == 0: print(&#8220;nAll Hypothesis tests passed.&#8221;) elif res.returncode == 5:<\/p>","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"pmpro_default_level":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"_pvb_checkbox_block_on_post":false,"footnotes":""},"categories":[52,5,7,1],"tags":[],"class_list":["post-84563","post","type-post","status-publish","format-standard","hentry","category-ai-club","category-committee","category-news","category-uncategorized","pmpro-has-access"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v25.3 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>A Coding Guide for Property-Based Testing Using Hypothesis with Stateful, Differential, and Metamorphic Test Design - YouZum<\/title>\n<meta name=\"description\" content=\"\u0e01\u0e34\u0e08\u0e01\u0e23\u0e23\u0e21\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a\u0e42\u0e14\u0e23\u0e19\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/youzum.net\/th\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/\" \/>\n<meta property=\"og:locale\" content=\"th_TH\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"A Coding Guide for Property-Based Testing Using Hypothesis with Stateful, Differential, and Metamorphic Test Design - YouZum\" \/>\n<meta property=\"og:description\" content=\"\u0e01\u0e34\u0e08\u0e01\u0e23\u0e23\u0e21\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a\u0e42\u0e14\u0e23\u0e19\" \/>\n<meta property=\"og:url\" content=\"https:\/\/youzum.net\/th\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/\" \/>\n<meta property=\"og:site_name\" content=\"YouZum\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/DroneAssociationTH\/\" \/>\n<meta property=\"article:published_time\" content=\"2026-04-19T15:19:17+00:00\" \/>\n<meta name=\"author\" content=\"admin NU\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin NU\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 \u0e19\u0e32\u0e17\u0e35\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/\"},\"author\":{\"name\":\"admin NU\",\"@id\":\"https:\/\/yousum.gpucore.co\/#\/schema\/person\/97fa48242daf3908e4d9a5f26f4a059c\"},\"headline\":\"A Coding Guide for Property-Based Testing Using Hypothesis with Stateful, Differential, and Metamorphic Test Design\",\"datePublished\":\"2026-04-19T15:19:17+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/\"},\"wordCount\":525,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/yousum.gpucore.co\/#organization\"},\"articleSection\":[\"AI\",\"Committee\",\"News\",\"Uncategorized\"],\"inLanguage\":\"th\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/\",\"url\":\"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/\",\"name\":\"A Coding Guide for Property-Based Testing Using Hypothesis with Stateful, Differential, and Metamorphic Test Design - YouZum\",\"isPartOf\":{\"@id\":\"https:\/\/yousum.gpucore.co\/#website\"},\"datePublished\":\"2026-04-19T15:19:17+00:00\",\"description\":\"\u0e01\u0e34\u0e08\u0e01\u0e23\u0e23\u0e21\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a\u0e42\u0e14\u0e23\u0e19\",\"breadcrumb\":{\"@id\":\"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/#breadcrumb\"},\"inLanguage\":\"th\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/youzum.net\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"A Coding Guide for Property-Based Testing Using Hypothesis with Stateful, Differential, and Metamorphic Test Design\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/yousum.gpucore.co\/#website\",\"url\":\"https:\/\/yousum.gpucore.co\/\",\"name\":\"YouSum\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/yousum.gpucore.co\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/yousum.gpucore.co\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"th\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/yousum.gpucore.co\/#organization\",\"name\":\"Drone Association Thailand\",\"url\":\"https:\/\/yousum.gpucore.co\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"th\",\"@id\":\"https:\/\/yousum.gpucore.co\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/youzum.net\/wp-content\/uploads\/2024\/11\/tranparent-logo.png\",\"contentUrl\":\"https:\/\/youzum.net\/wp-content\/uploads\/2024\/11\/tranparent-logo.png\",\"width\":300,\"height\":300,\"caption\":\"Drone Association Thailand\"},\"image\":{\"@id\":\"https:\/\/yousum.gpucore.co\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/DroneAssociationTH\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/yousum.gpucore.co\/#\/schema\/person\/97fa48242daf3908e4d9a5f26f4a059c\",\"name\":\"admin NU\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"th\",\"@id\":\"https:\/\/yousum.gpucore.co\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/youzum.net\/wp-content\/uploads\/avatars\/2\/1746849356-bpfull.png\",\"contentUrl\":\"https:\/\/youzum.net\/wp-content\/uploads\/avatars\/2\/1746849356-bpfull.png\",\"caption\":\"admin NU\"},\"url\":\"https:\/\/youzum.net\/th\/members\/adminnu\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"A Coding Guide for Property-Based Testing Using Hypothesis with Stateful, Differential, and Metamorphic Test Design - YouZum","description":"\u0e01\u0e34\u0e08\u0e01\u0e23\u0e23\u0e21\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a\u0e42\u0e14\u0e23\u0e19","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/youzum.net\/th\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/","og_locale":"th_TH","og_type":"article","og_title":"A Coding Guide for Property-Based Testing Using Hypothesis with Stateful, Differential, and Metamorphic Test Design - YouZum","og_description":"\u0e01\u0e34\u0e08\u0e01\u0e23\u0e23\u0e21\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a\u0e42\u0e14\u0e23\u0e19","og_url":"https:\/\/youzum.net\/th\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/","og_site_name":"YouZum","article_publisher":"https:\/\/www.facebook.com\/DroneAssociationTH\/","article_published_time":"2026-04-19T15:19:17+00:00","author":"admin NU","twitter_card":"summary_large_image","twitter_misc":{"Written by":"admin NU","Est. reading time":"7 \u0e19\u0e32\u0e17\u0e35"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/#article","isPartOf":{"@id":"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/"},"author":{"name":"admin NU","@id":"https:\/\/yousum.gpucore.co\/#\/schema\/person\/97fa48242daf3908e4d9a5f26f4a059c"},"headline":"A Coding Guide for Property-Based Testing Using Hypothesis with Stateful, Differential, and Metamorphic Test Design","datePublished":"2026-04-19T15:19:17+00:00","mainEntityOfPage":{"@id":"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/"},"wordCount":525,"commentCount":0,"publisher":{"@id":"https:\/\/yousum.gpucore.co\/#organization"},"articleSection":["AI","Committee","News","Uncategorized"],"inLanguage":"th","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/","url":"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/","name":"A Coding Guide for Property-Based Testing Using Hypothesis with Stateful, Differential, and Metamorphic Test Design - YouZum","isPartOf":{"@id":"https:\/\/yousum.gpucore.co\/#website"},"datePublished":"2026-04-19T15:19:17+00:00","description":"\u0e01\u0e34\u0e08\u0e01\u0e23\u0e23\u0e21\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a\u0e42\u0e14\u0e23\u0e19","breadcrumb":{"@id":"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/#breadcrumb"},"inLanguage":"th","potentialAction":[{"@type":"ReadAction","target":["https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/youzum.net\/a-coding-guide-for-property-based-testing-using-hypothesis-with-stateful-differential-and-metamorphic-test-design\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/youzum.net\/"},{"@type":"ListItem","position":2,"name":"A Coding Guide for Property-Based Testing Using Hypothesis with Stateful, Differential, and Metamorphic Test Design"}]},{"@type":"WebSite","@id":"https:\/\/yousum.gpucore.co\/#website","url":"https:\/\/yousum.gpucore.co\/","name":"YouSum","description":"","publisher":{"@id":"https:\/\/yousum.gpucore.co\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/yousum.gpucore.co\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"th"},{"@type":"Organization","@id":"https:\/\/yousum.gpucore.co\/#organization","name":"Drone Association Thailand","url":"https:\/\/yousum.gpucore.co\/","logo":{"@type":"ImageObject","inLanguage":"th","@id":"https:\/\/yousum.gpucore.co\/#\/schema\/logo\/image\/","url":"https:\/\/youzum.net\/wp-content\/uploads\/2024\/11\/tranparent-logo.png","contentUrl":"https:\/\/youzum.net\/wp-content\/uploads\/2024\/11\/tranparent-logo.png","width":300,"height":300,"caption":"Drone Association Thailand"},"image":{"@id":"https:\/\/yousum.gpucore.co\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/DroneAssociationTH\/"]},{"@type":"Person","@id":"https:\/\/yousum.gpucore.co\/#\/schema\/person\/97fa48242daf3908e4d9a5f26f4a059c","name":"admin NU","image":{"@type":"ImageObject","inLanguage":"th","@id":"https:\/\/yousum.gpucore.co\/#\/schema\/person\/image\/","url":"https:\/\/youzum.net\/wp-content\/uploads\/avatars\/2\/1746849356-bpfull.png","contentUrl":"https:\/\/youzum.net\/wp-content\/uploads\/avatars\/2\/1746849356-bpfull.png","caption":"admin NU"},"url":"https:\/\/youzum.net\/th\/members\/adminnu\/"}]}},"rttpg_featured_image_url":null,"rttpg_author":{"display_name":"admin NU","author_link":"https:\/\/youzum.net\/th\/members\/adminnu\/"},"rttpg_comment":0,"rttpg_category":"<a href=\"https:\/\/youzum.net\/th\/category\/ai-club\/\" rel=\"category tag\">AI<\/a> <a href=\"https:\/\/youzum.net\/th\/category\/committee\/\" rel=\"category tag\">Committee<\/a> <a href=\"https:\/\/youzum.net\/th\/category\/news\/\" rel=\"category tag\">News<\/a> <a href=\"https:\/\/youzum.net\/th\/category\/uncategorized\/\" rel=\"category tag\">Uncategorized<\/a>","rttpg_excerpt":"In this tutorial, we explore property-based testing using Hypothesis and build a rigorous testing pipeline that goes far beyond traditional unit testing. We implement invariants, differential testing, metamorphic testing, targeted exploration, and stateful testing to validate both functional correctness and behavioral guarantees of our systems. Instead of manually crafting edge cases, we let Hypothesis generate&hellip;","_links":{"self":[{"href":"https:\/\/youzum.net\/th\/wp-json\/wp\/v2\/posts\/84563","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/youzum.net\/th\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/youzum.net\/th\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/youzum.net\/th\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/youzum.net\/th\/wp-json\/wp\/v2\/comments?post=84563"}],"version-history":[{"count":0,"href":"https:\/\/youzum.net\/th\/wp-json\/wp\/v2\/posts\/84563\/revisions"}],"wp:attachment":[{"href":"https:\/\/youzum.net\/th\/wp-json\/wp\/v2\/media?parent=84563"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/youzum.net\/th\/wp-json\/wp\/v2\/categories?post=84563"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/youzum.net\/th\/wp-json\/wp\/v2\/tags?post=84563"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}