æåãã¹ããå ·äœäŸããŒã¹ã®ãã¹ãã§ã¯èŠéããã¡ãªãšããžã±ãŒã¹ãèªåã§èŠã€ãã匷åãªããŒã«
ã¯ããã«ïŒHypothesisãšã¯ïŒ ð€
Hypothesisã¯ãPythonã§å©çšã§ãã匷åãªããããã£ããŒã¹ãã¹ãã®ã©ã€ãã©ãªã§ããåŸæ¥ã®å ·äœäŸã«åºã¥ãããã¹ãïŒExample-Based TestingïŒã§ã¯ãéçºè ãæ³å®ããç¹å®ã®ã±ãŒã¹ããæ€èšŒã§ããŸãããããããHypothesisã䜿ããšãã³ãŒããæºããã¹ããæ§è³ªïŒããããã£ïŒããå®çŸ©ããã ãã§ãã©ã€ãã©ãªããã®æ§è³ªãæºããå€çš®å€æ§ãªãã¹ãããŒã¿ãèªåçæãããã¹ããå®è¡ããŠãããŸããããã«ãããéçºè ãæãããããªãã£ããšããžã±ãŒã¹ãå¢çå€ã®åé¡ãçºèŠãããããªããŸãã
Haskellã®QuickCheckã©ã€ãã©ãªã«ãã£ãŠåºãŸã£ããã¹ãææ³ã§ããåå¥ã®ãã¹ãã±ãŒã¹ãèšè¿°ãã代ããã«ãã³ãŒããæã€ã¹ãæ®éçãªæ§è³ªïŒäŸãã°ãããªã¹ãããœãŒãããŠãèŠçŽ æ°ã¯å€ãããªããããšã³ã³ãŒãããããŒã¿ããã³ãŒãããã°å ã«æ»ãããªã©ïŒãå®çŸ©ããŸãããã¹ãã©ã€ãã©ãªã¯ããã®æ§è³ªãç Žããããªå ¥åããŒã¿ãèªåçæããããšè©Šã¿ãŸãã
Hypothesisã®äž»ãªå©ç¹ã¯ä»¥äžã®éãã§ãã
- ð ãã¹ãèšè¿°ã®ç°¡ç¥å: å ·äœçãªãã¹ãããŒã¿ãèããæéãçããŸãã
- ð ãšããžã±ãŒã¹ã®çºèŠ: äºæããªãå ¥åãã¿ãŒã³ãçæããé ãããã°ãçºèŠããŸãã
- ð æå°åäŸã®æ瀺 (Shrinking): ãã¹ãã倱æããå Žåãåé¡ãåçŸããæãã·ã³ãã«ãªå ¥åå€ãç¹å®ããŠãããŸããããã«ãããããã°ã容æã«ãªããŸãã
- 𧩠æ¢åãã¹ããšã®é£æº: pytestãunittestãªã©ã®æ¢åã®ãã¹ããã¬ãŒã ã¯ãŒã¯ãšç°¡åã«çµ±åã§ããŸãã
- ð¡ïž ã³ãŒãã®å ç¢æ§åäž: ããåºç¯ãªå ¥åã«å¯Ÿããã³ãŒãã®æ¯ãèããæ€èšŒããä¿¡é Œæ§ãé«ããŸãã
ãã®ããã°ã§ã¯ãHypothesisã®åºæ¬çãªäœ¿ãæ¹ãããããé«åºŠãªãã¯ããã¯ãŸã§ã詳ãã解説ããŠãããŸãã
åºæ¬çãªäœ¿ãæ¹ïŒ@givenãšã¹ãã©ããžãŒ âïž
Hypothesisã®æãåºæ¬çãªäœ¿ãæ¹ã¯ã@given
ãã³ã¬ãŒã¿ãšã¹ãã©ããžãŒïŒStrategiesïŒãçµã¿åãããããšã§ãã
ã¹ãã©ããžãŒã¯ãHypothesisãã©ã®ããã«ãã¹ãããŒã¿ãçæããããå®çŸ©ãããã®ã§ããæŽæ°ãæµ®åå°æ°ç¹æ°ãæååããªã¹ããèŸæžãªã©ãæ§ã ãªçµã¿èŸŒã¿ã¹ãã©ããžãŒãçšæãããŠããŸãã
@given
ãã³ã¬ãŒã¿ã¯ããã¹ãé¢æ°ã«é©çšããåŒæ°ãšããŠäœ¿çšããã¹ãã©ããžãŒãæå®ããŸããHypothesisã¯æå®ãããã¹ãã©ããžãŒã«åºã¥ããŠããŒã¿ãçæãããã¹ãé¢æ°ãè€æ°åïŒããã©ã«ãã§ã¯100åïŒåŒã³åºããŸãã
ã€ã³ã¹ããŒã«
ãŸããHypothesisãã€ã³ã¹ããŒã«ããŸãããã
pip install hypothesis
pytestãªã©ã®ãã¹ãã©ã³ããŒãšäžç·ã«äœ¿ãããšãå€ãã§ãããã
pip install pytest hypothesis
ç°¡åãªäŸïŒæŽæ°ã®ãã¹ã
äŸãšããŠã2ã€ã®æŽæ°ã®åèšãèšç®ããç°¡åãªé¢æ°ããã¹ãããŠã¿ãŸãããã
# calculator.py
def add(x, y):
return x + y
ãã®é¢æ°ã«å¯ŸããŠãHypothesisã䜿ã£ãŠããããã£ããŒã¹ãã¹ããèšè¿°ããŸããããã§ã¯ããå ç®ã®é åºãå€ããŠãçµæã¯åãïŒå¯ææ³åïŒããšããæ§è³ªããã¹ãããŸãã
# test_calculator.py
from hypothesis import given, strategies as st
from calculator import add
@given(st.integers(), st.integers())
def test_addition_is_commutative(x, y):
assert add(x, y) == add(y, x)
# pytestãå®è¡ãããšãHypothesisãèªåã§xãšyã«æ§ã
ãªæŽæ°ãçæããŠãã¹ãããŸãã
st.integers()
ã¯ãä»»æã®æŽæ°ãçæããã¹ãã©ããžãŒã§ãã@given(st.integers(), st.integers())
ã«ãããHypothesisã¯x
ãšy
ã«æ§ã
ãªæŽæ°ã®çµã¿åããïŒæ£ãè² ããŒãã倧ããªå€ãå°ããªå€ãªã©ïŒã代å
¥ããŠtest_addition_is_commutative
ãå®è¡ããŸãã
ãã䜿ãããçµã¿èŸŒã¿ã¹ãã©ããžãŒ
Hypothesisã«ã¯å€æ°ã®äŸ¿å©ãªçµã¿èŸŒã¿ã¹ãã©ããžãŒããããŸããããã€ãäŸãæããŸãã
ã¹ãã©ããžãŒ | çæããããŒã¿ | äŸ |
---|---|---|
st.integers() | æŽæ° | @given(st.integers(min_value=0, max_value=100)) (0ãã100ãŸã§ã®æŽæ°) |
st.floats() | æµ®åå°æ°ç¹æ° | @given(st.floats(allow_nan=False, allow_infinity=False)) (NaNãç¡é倧ãå«ãŸãªã) |
st.booleans() | çåœå€ (True/False) | @given(st.booleans()) |
st.text() | æåå | @given(st.text(min_size=1, alphabet=st.characters(whitelist_categories=('Lu', 'Ll')))) (ã¢ã«ãã¡ãããã®ã¿ã®1æå以äžã®æåå) |
st.lists() | ãªã¹ã | @given(st.lists(st.integers(), min_size=1, max_size=10)) (1ãã10åã®æŽæ°ãå«ããªã¹ã) |
st.tuples() | ã¿ãã« | @given(st.tuples(st.integers(), st.text())) (æŽæ°ãšæååã®ãã¢ã®ã¿ãã«) |
st.dictionaries() | èŸæž | @given(st.dictionaries(st.text(max_size=5), st.booleans())) (ããŒãæ倧5æåã®æååãå€ãçåœå€ã®èŸæž) |
st.datetimes() | datetimeãªããžã§ã¯ã | @given(st.datetimes()) |
st.none() | None | @given(st.none()) |
st.binary() | ãã€ããªããŒã¿ (bytes) | @given(st.binary(max_size=1024)) |
st.sampled_from() | äžãããããªã¹ããã¿ãã«ããèŠçŽ ãéžæ | @given(st.sampled_from(["apple", "banana", "cherry"])) |
st.one_of() | è€æ°ã®ã¹ãã©ããžãŒã®ããããäžã€ãéžæ | @given(st.one_of(st.integers(), st.text())) (æŽæ°ãŸãã¯æåå) |
ãããã®ã¹ãã©ããžãŒã¯ãåŒæ°ã䜿ã£ãŠçæãããããŒã¿ã®ç¯å²ãç¹æ§ã现ããå¶åŸ¡ã§ããŸããäŸãã°ãst.integers(min_value=0)
ãšããã°éè² æŽæ°ã®ã¿ãçæãããŸãã
ShrinkingïŒå€±æäŸã®åçŽå ð¬
Hypothesisã®éåžžã«åŒ·åãªæ©èœã®äžã€ãShrinkingïŒã·ã¥ãªã³ãã³ã°ïŒã§ãããã¹ãã倱æããå ¥åããŒã¿ãèŠã€ãã£ãå ŽåãHypothesisã¯ãã®ããŒã¿æ§é ãä¿ã¡ã€ã€ãå¯èœãªéããåçŽãªã圢ã«çž®å°ããããšè©Šã¿ãŸãã
äŸãã°ããããªã¹ãåŠçé¢æ°ã [0, 0, 1, 0, 0]
ãšããå
¥åã§ãã°ãèµ·ããããšããŸããHypothesisã¯ããã®ãªã¹ããçž®å°ããäŸãã° [0, 0]
ã®ãããªãããçããããåçŽãªå€ã§ãåããã°ãåçŸã§ããããæ¢ããŸããæçµçã«ããã°ãåçŸããæãåçŽãªïŒæå°ã®ïŒåäŸãå ±åããŠããããããéçºè
ã¯åé¡ã®åå ãç¹å®ãããããªããŸãã
from hypothesis import given, strategies as st
def buggy_sort(data):
# ç°¡åãªäŸïŒãªã¹ãã«éè€ããããšæ£ãããœãŒãã§ããªããã°
if len(data) != len(set(data)):
# ãããšééã£ããœãŒãããã
return sorted(data, reverse=True)
return sorted(data)
@given(st.lists(st.integers(min_value=0, max_value=10)))
def test_sort_preserves_elements(data):
original_set = set(data)
sorted_data = buggy_sort(data)
sorted_set = set(sorted_data)
# èŠçŽ ã倱ããããè¿œå ããããããŠããªãããã§ãã¯
assert original_set == sorted_set
# buggy_sortã¯éè€ããããšãœãŒãé ãéã«ãªãã®ã§ãããããã§ãã¯
# assert sorted_data == sorted(data) # ãã®ã¢ãµãŒãã¯éè€ããããšå€±æãã
# pytestãå®è¡ãããš...
# Falsifying example: test_sort_preserves_elements(
# data=[0, 0] # HypothesisãåçŽåãã倱æäŸïŒ
# )
# å
ã
ã¯ãã£ãšè€éãªãªã¹ãã§å€±æãããããããªããã[0, 0]ã«çž®å°ãããã
ãã®Shrinkingæ©èœã«ããããããã°ããã»ã¹ãå€§å¹ ã«å¹çåãããŸããè€éãªããŒã¿æ§é ã§çºçãããã°ãããã®æ¬è³ªã瀺ãã·ã³ãã«ãªã±ãŒã¹ã«èœãšã蟌ãã§ãããã®ã§ããð
é«åºŠãªæ©èœãšãã¯ãã㯠ð
è€åã¹ãã©ããžãŒ (@composite)
æ¢åã®ã¹ãã©ããžãŒãçµã¿åãããŠãããè€éãªããŒã¿æ§é ããäºãã«é¢é£æ§ãæã€ããŒã¿ãçæãããå ŽåããããŸãããã®ãããªå Žåã«@composite
ãã³ã¬ãŒã¿ã圹ç«ã¡ãŸãã
@composite
ã§è£
食ãããé¢æ°ã¯ãæ°ããã¹ãã©ããžãŒãå®çŸ©ããŸãããã®é¢æ°ã¯ã第äžåŒæ°ãšããŠdraw
é¢æ°ãåãåããŸããdraw
é¢æ°ã¯ãä»ã®ã¹ãã©ããžãŒãåŒæ°ã«åãããã®ã¹ãã©ããžãŒããå€ããåŒãåºãããŸãã
äŸïŒé·ããšãã®é·ããæã€ãªã¹ããçæããã¹ãã©ããžãŒ
from hypothesis import given, strategies as st, composite
@composite
def list_and_length(draw):
# ãŸããªã¹ãã®é·ããæ±ºå® (äŸ: 0ãã10)
length = draw(st.integers(min_value=0, max_value=10))
# 次ã«ã決å®ããé·ããæã€æŽæ°ã®ãªã¹ããçæ
data_list = draw(st.lists(st.integers(), min_size=length, max_size=length))
return (length, data_list)
@given(list_and_length())
def test_list_length(length_and_list):
length, data_list = length_and_list
print(f"Generated: length={length}, list={data_list}")
assert len(data_list) == length
ãã®äŸã§ã¯ããŸãdraw(st.integers(min_value=0, max_value=10))
ã§ãªã¹ãã®é·ãã決å®ãã次ã«ãã®é·ãã䜿ã£ãŠdraw(st.lists(st.integers(), min_size=length, max_size=length))
ã§ãªã¹ãèªäœãçæããŠããŸãããã®ããã«ã@composite
ã䜿ãããšã§ãçæããã»ã¹ã«äŸåé¢ä¿ãæãããããšãã§ããŸãã
ã¹ããŒããã«ãã¹ã (Stateful Testing / RuleBasedStateMachine)
ãªããžã§ã¯ãæåããã°ã©ãã³ã°ããç¶æ
ãæã€ã·ã¹ãã ã®ãã¹ãã§ã¯ãåäžã®é¢æ°åŒã³åºãã ãã§ãªããäžé£ã®æäœïŒã¡ãœããåŒã³åºãïŒã®çµæãæ€èšŒãããå ŽåããããŸããHypothesisã®ã¹ããŒããã«ãã¹ãæ©èœïŒRuleBasedStateMachine
ïŒã¯ããã®ãããªã·ããªãªã«å¯Ÿå¿ããŸãã
ã¹ããŒããã«ãã¹ãã§ã¯ããã¹ã察象ã®ã·ã¹ãã ã«å¯ŸããæäœïŒã«ãŒã«ïŒãšãåæäœã§çæããããŒã¿ãå®çŸ©ããŸããHypothesisã¯ããããã®ã«ãŒã«ãæ§ã ãªé åºã§ãæ§ã ãªããŒã¿ãçšããŠå®è¡ããã·ã¹ãã ã®ç¶æ ãäºæãã¬ç¶æ ã«ãªããªããããããã¯å®çŸ©ãããäžå€æ¡ä»¶ïŒinvariantïŒãåžžã«ä¿ãããããæ€èšŒããŸãã
äŸïŒç°¡åãªã«ãŠã³ã¿ãŒã¯ã©ã¹ã®ãã¹ã
from hypothesis import strategies as st
from hypothesis.stateful import RuleBasedStateMachine, rule, precondition, invariant
class Counter:
def __init__(self):
self.value = 0
def increment(self, amount):
if amount < 0:
raise ValueError("Amount must be non-negative")
self.value += amount
def decrement(self, amount):
if amount < 0:
raise ValueError("Amount must be non-negative")
if self.value - amount < 0:
# æ¬æ¥ã¯ãšã©ãŒã«ãã¹ããããããªãããããã§ã¯0æªæºã«ãªããªãããã«ãã
self.value = 0
else:
self.value -= amount
def get_value(self):
return self.value
class CounterStateMachine(RuleBasedStateMachine):
def __init__(self):
super().__init__()
self.counter = Counter()
@rule(amount=st.integers(min_value=0, max_value=10))
def increment_counter(self, amount):
self.counter.increment(amount)
# decrementã¯valueãamount以äžã®å Žåã®ã¿å®è¡å¯èœãšããäºåæ¡ä»¶
@precondition(lambda self: self.counter.get_value() >= 0)
@rule(amount=st.integers(min_value=0, max_value=10))
def decrement_counter(self, amount):
# decrementã¡ãœããã0æªæºã«ãªããªãããšãæåŸ
ããŠãã
current_value = self.counter.get_value()
self.counter.decrement(amount)
# ããã§ã¯decrementã®å®è£
ã«ãã0æªæºã«ãªããªãã¯ã
# assert self.counter.get_value() < current_value if amount > 0 and current_value > 0 else True
# äžå€æ¡ä»¶ïŒã«ãŠã³ã¿ãŒã®å€ã¯åžžã«éè² ã§ããã¹ã
@invariant()
def value_is_non_negative(self):
assert self.counter.get_value() >= 0
# pytestãªã©ã§ãã¹ãã¯ã©ã¹ãšããŠå®è¡ãã
TestCounter = CounterStateMachine.TestCase
ãã®äŸã§ã¯ãCounter
ã¯ã©ã¹ã«å¯ŸããŠincrement
ãšdecrement
ãšããæäœïŒã«ãŒã«ïŒãå®çŸ©ããåžžã«value
ãéè² ã§ãããšããäžå€æ¡ä»¶ïŒinvariantïŒãèšå®ããŠããŸããHypothesisã¯increment_counter
ãšdecrement_counter
ã«ãŒã«ãæ§ã
ãªé åºã»ããŒã¿ã§å®è¡ããvalue_is_non_negative
ãç Žãããã·ãŒã±ã³ã¹ããªãããæ¢ããŸãã@precondition
ã䜿ããšãç¹å®ã®ã«ãŒã«ãå®è¡ãããããã®æ¡ä»¶ãæå®ã§ããŸãã
ã¹ããŒããã«ãã¹ãã¯ãç¶æ é·ç§»ãè€éãªã·ã¹ãã ããAPIã®çžäºäœçšãªã©ããã¹ãããéã«éåžžã«æå¹ã§ãã
ããŒã¿ã®ãã£ã«ã¿ãªã³ã° (assume)
ã¹ãã©ããžãŒãçæããããŒã¿ã®äžã«ã¯ããã¹ãã®åææ¡ä»¶ãæºãããªããã®ïŒäŸãã°ããŒãé€ç®ãåŒãèµ·ãããŒããªã©ïŒãå«ãŸããããšããããŸãããã®ãããªç¡å¹ãªããŒã¿ããã¹ãå®è¡åã«é€å€ãããå Žåãhypothesis.assume()
é¢æ°ã䜿çšããŸãã
from hypothesis import given, strategies as st, assume
def divide(a, b):
# bã0ã ãšZeroDivisionErrorãçºçãã
return a / b
@given(st.integers(), st.integers())
def test_division_properties(a, b):
# bã0ã®å Žåã¯ãã¹ããå®è¡ããªã
assume(b != 0)
result = divide(a, b)
# ç°¡åãªããããã£: a / b * b == a (æµ®åå°æ°ç¹æ°ã®èª€å·®ã¯ç¡èŠ)
# å®éã«ã¯èª€å·®ãèæ
®ããæ¯èŒãå¿
èŠ
import math
assert math.isclose(result * b, a)
assume(b != 0)
ãè©äŸ¡ãããæ¡ä»¶ãåœïŒã€ãŸãb
ã0ïŒã®å ŽåãHypothesisã¯ãã®ãã¹ãã±ãŒã¹ãç Žæ£ãã次ã®ããŒã¿çæã«ç§»ããŸããããã«ããããã¹ãé¢æ°æ¬äœã§ã¯åææ¡ä»¶ãæºããããŠããããŒã¿ã®ã¿ãæ±ãããšãã§ããŸãããã ããassume
ã䜿ãããããšãæå¹ãªãã¹ãã±ãŒã¹ãååã«çæãããªããªãå¯èœæ§ãããã®ã§æ³šæãå¿
èŠã§ããð§
ç¹å®ã®äŸã®ãã¹ã (@example)
ããããã£ããŒã¹ãã¹ãã¯ã©ã³ãã ãªããŒã¿ãçæããŸãããç¹å®ã®æ¢ç¥ã®ãšããžã±ãŒã¹ãéèŠãªå€ãå¿
ããã¹ããããå ŽåããããŸãããã®ãããªå Žåã«ã¯@example
ãã³ã¬ãŒã¿ã䜿çšããŸãã
from hypothesis import given, strategies as st, example
def get_grade(score):
if score < 0 or score > 100:
raise ValueError("Score must be between 0 and 100")
if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
elif score >= 60:
return "D"
else:
return "F"
@given(st.integers(min_value=0, max_value=100))
@example(0) # å¢çå€ 0 ãå¿
ããã¹ã
@example(59) # FãšDã®å¢ç
@example(60) # DãšCã®å¢ç
@example(89) # BãšAã®å¢ç
@example(90) # Aã®å¢ç
@example(100) # å¢çå€ 100 ãå¿
ããã¹ã
def test_grade_boundaries(score):
grade = get_grade(score)
assert isinstance(grade, str)
assert grade in ["A", "B", "C", "D", "F"]
# ããã§ããã«è©³çŽ°ãªããããã£ããã§ãã¯ã§ãã
if score < 60:
assert grade == "F"
elif score < 70:
assert grade == "D"
# ...ãªã©
@example(...)
ã§æå®ãããå€ã¯ãHypothesisãã©ã³ãã ã«çæããããŒã¿ã®åã«ãå¿
ããã¹ãé¢æ°ã«æž¡ãããŸããããã«ãããéèŠãªã±ãŒã¹ããã¹ãããæŒããããšãé²ããŸããð
ãã¹ãèšå®ã®èª¿æŽ (settings)
Hypothesisã®æåã¯@settings
ãã³ã¬ãŒã¿ã䜿ã£ãŠèª¿æŽã§ããŸããäŸãã°ãçæãããã¹ãã±ãŒã¹ã®æ°ãå¢ããããããã¹ãã®å®è¡æéãå¶éãããã§ããŸãã
from hypothesis import given, strategies as st, settings, Verbosity
@settings(
max_examples=500, # çæããäŸã®æ倧æ°ã500ã«å¢ãã (ããã©ã«ãã¯100)
deadline=1000, # ãã¹ãå
šäœã®å®è¡æéã1000ããªç§ (1ç§) ã«å¶é
verbosity=Verbosity.verbose # å®è¡äžã®è©³çŽ°æ
å ±ã衚瀺
)
@given(st.text())
def test_string_processing(s):
# äœããã®æåååŠç
processed = s.upper()
assert len(processed) == len(s)
å©çšå¯èœãªèšå®é ç®ã¯å€æ°ããããã¹ãã®ç¶²çŸ æ§ãå®è¡æéã®ãã¬ãŒããªãã調æŽããã®ã«åœ¹ç«ã¡ãŸãã詳现ã¯å ¬åŒããã¥ã¡ã³ãã®Settingsã®ããŒãžãåç §ããŠãã ããã
Hypothesisã®å©ç¹ãšèæ ®äºé â ð€
å©ç¹ (Pros)
- ãã°çºèŠèœåã®åäž: æåã§ã¯æãã€ããªããããªãšããžã±ãŒã¹ãã³ãŒããŒã±ãŒã¹ãèªåçã«çºèŠã§ããŸããç¹ã«ãæ°å€èšç®ãããŒã¿åŠçãè€éãªç¶æ é·ç§»ãªã©ãæã€ã³ãŒãã«å¯ŸããŠåŒ·åã§ãã
- ãã¹ãã³ãŒãã®åæž: å€ãã®ãã¹ãã±ãŒã¹ãææžãããå¿ èŠããªããªãã代ããã«ã³ãŒããæºããã¹ããæ§è³ªãã«éäžã§ããŸããããã«ããããã¹ãã³ãŒãã®éãæžããã¡ã³ããã³ã¹æ§ãåäžããããšããããŸãã
- ååž°ãã¹ãã®åŒ·å: äžåºŠçºèŠããããã°ïŒFalsifying exampleïŒã¯Hypothesisã«ãã£ãŠããŒã¿ããŒã¹ã«ä¿åããã次å以éã®ãã¹ãå®è¡æã«èªåçã«åå®è¡ãããŸããããã«ãããä¿®æ£ãããã°ãåçºããŠããªããã確å®ã«ãã§ãã¯ã§ããŸãã
- ä»æ§ã®æ確å: ã³ãŒãã®ãæ§è³ªããèããããã»ã¹ã¯ãã³ãŒããäœããã¹ãããã©ã®ãããªæ¡ä»¶äžã§åäœãã¹ãããšããä»æ§ãããæ·±ãç解ããæ確ã«ããå©ããšãªããŸãã
- ãããã°ã®å®¹æå: Shrinkingæ©èœã«ããããã°ãåçŸããæå°ã®å ¥åãæäŸããããããåé¡ã®åå ç¹å®ã容æã«ãªããŸãã
èæ ®äºé (Cons / Challenges)
- é©åãªãæ§è³ªãã®èæ¡: æãé£ããç¹ã¯ããã¹ã察象ã®ã³ãŒããæºããã¹ãæ®éçãªãæ§è³ªããèŠã€ãåºãããšã§ããèªæã§ãªãæ§è³ªããè€éãªã·ã¹ãã ã®æ§è³ªãå®çŸ©ããã®ã¯é£ããå ŽåããããŸããåã«é¢æ°ãã¯ã©ãã·ã¥ããªãããšã確èªããã ãã§ã䟡å€ã¯ãããŸãããããæ·±ãæ§è³ªããã¹ãããããšã§ãããå€ãã®ãã°ãçºèŠã§ããŸãã
- ãã¹ãå®è¡æé: Hypothesisã¯å€æ°ã®ãã¹ãã±ãŒã¹ãçæã»å®è¡ãããããåŸæ¥ã®å
·äœäŸããŒã¹ã®ãã¹ããããå®è¡ã«æéããããããšããããŸãã
@settings
ã§èª¿æŽå¯èœã§ãããCI/CDãã€ãã©ã€ã³ãªã©ã§ã®å®è¡æéã«ã¯æ³šæãå¿ èŠã§ãã - åŠç¿ã³ã¹ã: ã¹ãã©ããžãŒã®ã«ã¹ã¿ãã€ãºãè€åã¹ãã©ããžãŒãã¹ããŒããã«ãã¹ããªã©ãé«åºŠãªæ©èœã«ã¯åŠç¿ãå¿ èŠã§ãã
- Shrinkingã®éç: åžžã«å®ç§ãªæå°åäŸãèŠã€ããããããã§ã¯ãããŸãããè€éãªäŸåé¢ä¿ãæã€ããŒã¿æ§é ã®å ŽåãShrinkingãæåŸ éãã«æ©èœããªãããšããããŸãã
- ãã¹ã察象ã«ãã£ãŠã¯äžåããªå Žåã: UIãã¹ãããéåžžã«å¯äœçšã®å€§ããåŠçïŒå€éšAPIãžã®æžã蟌ã¿ãªã©ïŒã®ãã¹ãã«ã¯ããã®ãŸãŸé©çšããã®ãé£ããå ŽåããããŸããïŒãã ããã¹ããŒããã«ãã¹ããªã©ã工倫ããŠé©çšã§ããã±ãŒã¹ããããŸãïŒ
Hypothesisã¯äžèœè¬ã§ã¯ãããŸããããåŸæ¥ã®ãã¹ãææ³ã匷åã«è£å®ããããŒã«ã§ããé©åã«å©çšããã°ããœãããŠã§ã¢ã®å質ãšéçºå¹çãå€§å¹ ã«åäžãããããšãã§ããŸããð
å®éã®äœ¿çšäŸãšãã¹ããã©ã¯ãã£ã¹ ð
ã©ã®ãããªå Žé¢ã§ç¹ã«æå¹ãïŒ
- ããŒã¿åŠçã»å€æã©ã€ãã©ãª: å ¥åããŒã¿ã®åœ¢åŒãå€ãå€å²ã«ãããå Žåã (äŸ: JSONããŒãµãŒãã·ãªã¢ã©ã€ã¶ãŒãããŒã¿ã¯ãªãŒãã³ã°é¢æ°)
- æ°å€èšç®ã»ç§åŠæè¡èšç®: æµ®åå°æ°ç¹æ°ã®ç²ŸåºŠåé¡ãå¢çå€ãç¹æ®ãªå€ïŒNaN, InfinityïŒãåé¡ã«ãªããããå Žåã
- ã¢ã«ãŽãªãºã å®è£ : ãœãŒããæ¢çŽ¢ãã°ã©ãã¢ã«ãŽãªãºã ãªã©ãå ¥åã«ãã£ãŠæ¯ãèãã倧ããå€ãããã®ãæ§è³ªïŒäŸ: ãœãŒãåŸãªã¹ãã¯æé ãèŠçŽ æ°ã¯äžå€ïŒãå®çŸ©ããããã
- ç¶æ ãæã€ãªããžã§ã¯ããã·ã¹ãã : ç¶æ é·ç§»ãè€éã§ãæäœã®é åºã«ãã£ãŠäºæãã¬æ¯ãèããèµ·ããå¯èœæ§ãããå ŽåãïŒã¹ããŒããã«ãã¹ããæå¹ïŒ
- ãããã³ã«å®è£ ã»ããŒãµãŒ: ä»æ§ã«æºæ ããŠããããäžæ£ãªå ¥åã«å¯Ÿããèæ§ãããããªã©ãæ€èšŒããå Žåã
- APIã¯ã©ã€ã¢ã³ã/ãµãŒããŒ: ãªã¯ãšã¹ããšã¬ã¹ãã³ã¹ã®æŽåæ§ããšã©ãŒãã³ããªã³ã°ãªã©ãæ€èšŒããå ŽåãïŒSchemathesisã®ãããªãHypothesisãå éšã§å©çšããAPIãã¹ãç¹åããŒã«ãååšããŸãïŒ
ãã¹ããã©ã¯ãã£ã¹
- å°ããå§ãã: ãŸãã¯åçŽãªé¢æ°ããèªæãªæ§è³ªïŒã¯ã©ãã·ã¥ããªããåãå€ãããªããªã©ïŒãããã¹ããå°å ¥ããŠã¿ãŸãããã
- æ§è³ªïŒPropertyïŒãæ確ã«ãã: äœããã¹ããããã®ããã³ãŒããä¿èšŒãã¹ãäžå€æ¡ä»¶ã¯äœããèããŸãããå ¥åXã«å¯ŸããŠãåžžã«Yãšããæ§è³ªãæãç«ã€ã¯ãã ããšãã圢åŒã§èãããšè¯ãã§ãããã
-
é©åãªã¹ãã©ããžãŒãéžã¶: çµã¿èŸŒã¿ã¹ãã©ããžãŒãç解ãããã¹ã察象ã®å
¥åã«æãè¿ããã®ãéžã³ãŸããå¿
èŠã«å¿ããŠç¯å²ãéå®ãããã
filter
ãmap
ã@composite
ã§ã«ã¹ã¿ãã€ãºããŸãã -
assume
ã¯æ§ããã«:assume
ã§ããŒã¿ããã£ã«ã¿ãªã³ã°ãããããšããã¹ãã«ãã¬ããžãäœäžããããHypothesisãæå¹ãªäŸãèŠã€ãã«ãããªãå¯èœæ§ããããŸããå¯èœãªéããã¹ãã©ããžãŒèªäœã調æŽããŠæå¹ãªããŒã¿ãçæãããããã«åªããŸãããã -
å¢çå€ãæèãã: ã¹ãã©ããžãŒã¯èªåã§å¢çå€ïŒ0, -1, 空æåå, 空ãªã¹ããªã©ïŒãçæããããšããŸãããç¹ã«éèŠã ãšããã£ãŠããå¢çå€ã¯
@example
ã§æ瀺çã«ãã¹ãã«è¿œå ãããšè¯ãã§ãããã - ãã¹ã倱ææã®æ å ±ã掻çšãã: Hypothesisãå ±åãã Falsifying example ã¯éåžžã«æçã§ããShrinkingã«ãã£ãŠåçŽåãããŠããããããããã°ã®æãããã«ãªããŸãã
- æ¢åã®ãã¹ããšçµã¿åããã: Hypothesisã¯å ·äœäŸããŒã¹ã®ãã¹ãã眮ãæãããã®ã§ã¯ãªããè£å®ãããã®ã§ããéèŠãªã·ããªãªã¯å ·äœäŸãã¹ãã§ã«ããŒãã€ã€ãHypothesisã§ç¶²çŸ æ§ãé«ããã®ãå¹æçã§ãã
- ããã¥ã¡ã³ããèªã: Hypothesisã®å ¬åŒããã¥ã¡ã³ãã¯éåžžã«å å®ããŠããŸããã¹ãã©ããžãŒã®è©³çŽ°ãé«åºŠãªæ©èœãèšå®ãªãã·ã§ã³ãªã©ãå€ãã®æ å ±ãèšèŒãããŠããŸããå°ã£ãããŸãããã¥ã¡ã³ããåç §ããŸãããã (Hypothesis Documentation)
ãŸãšã ð
Hypothesisã¯ãPythonã«ãããããããã£ããŒã¹ãã¹ãã®ããã®åŒ·åã§æè»ãªã©ã€ãã©ãªã§ãããã¹ãããŒã¿ã®èªåçæããšããžã±ãŒã¹ã®çºèŠã倱æäŸã®åçŽåïŒShrinkingïŒãã¹ããŒããã«ãã¹ããšãã£ãæ©èœãéããŠããœãããŠã§ã¢ã®å質åäžãšéçºå¹çã®æ¹åã«å€§ããè²¢ç®ããŸãã
é©åãªãæ§è³ªããèŠã€ããããšã«ã¯æ £ããå¿ èŠãããããŸããããäžåºŠãã®èãæ¹ã身ã«ã€ããã°ãåŸæ¥ã®ãã¹ãææ³ã§ã¯èŠéããã¡ã ã£ãå€ãã®ãã°ãçºèŠã§ããããã«ãªããŸãã
ãã²ãããªãã®ãããžã§ã¯ãã«Hypothesisãå°å ¥ããããå ç¢ã§ä¿¡é Œæ§ã®é«ãã³ãŒããç®æããŠã¿ãŠãã ããïŒ ðªð
ã³ã¡ã³ã