4.5.17 · Coding › Software Engineering
Intuition Sochne ka tarika badal jaata hai
Normal ("example-based") tests kehte hain: "IS input ke liye, mujhe YE output chahiye." Property-based testing kehti hai: "kisi bhi valid input ke liye, yeh rule hamesha hold karna chahiye." Tumhare 3 examples haath se chunne ki jagah, framework tumhare code par saikdon random inputs phenkhta hai aur tumhara stated rule todne ki koshish karta hai.
YE kyun zaroori hai: Tum sirf wahi inputs sochte ho jo tum imagine karte ho. Bugs un inputs mein rehte hain jo tum bhool gaye (empty list, negative zero, Unicode, bahut bade numbers). Machine random data generate karke woh edge cases tumhare liye dhundh leti hai.
Definition Property-based testing (PBT)
Ek testing technique jisme tum properties specify karte ho (invariants jo sabhi valid inputs ke liye hold karni chahiye) aur ek framework bahut saare random inputs generate karta hai, har ek par property check karta hai, aur failure hone par shrinking perform karta hai taaki sabse chhota counterexample report kar sake.
Key vocabulary:
Property ::= ek boolean rule jo har valid input ke liye true hai.
Generator ::= woh code jo random valid inputs produce karta hai (integers(), lists(), text()).
Shrinking ::= ek failing input milne ke baad, framework use minimal example tak simplify karta hai jo phir bhi fail karta hai.
Worked example Ek list ko reverse karna
Example-based:
assert reverse([ 1 , 2 , 3 ]) == [ 3 , 2 , 1 ] # one input, one output
Weak kyun hai? Tab bhi pass hoti hai agar reverse sirf length-3 lists ke liye kaam kare.
Property-based (Hypothesis use karke):
from hypothesis import given
from hypothesis.strategies import lists, integers
@given (lists(integers()))
def test_reverse_twice_is_identity (xs):
assert reverse(reverse(xs)) == xs # property for ALL xs
Yeh step kyun? reverse(reverse(xs)) == xs ek mathematical truth hai jo values se independent hai, isliye yeh har generated list ke liye hold karni chahiye — empty, single, huge, negative.
Tumhe exact output hamesha pata nahi hota, lekin tum usually ek relationship jaante ho. Common patterns:
"RIIOM" properties: R ound-trip, I dempotence, I nvariant, O racle, M etamorphic.
Intuition Shrinking kyun matter karta hai
Ek random failing input shayad [847, -3, 901, 0, -12, ...] ho. Debugging ke liye bilkul bekaar. Shrinking baar baar chhote/simple inputs try karta hai (kam elements, 0 ke kareeb values) jo phir bhi fail karte hain , converge karke e.g. [0, -1] tak. Woh minimal counterexample seedha bug ki taraf point karta hai.
Shrink loop ka mental model:
Failing input x 0 mila.
Ek "simpler" candidate x ′ propose karo (ek element drop karo, ek number halve karo).
Agar x ′ phir bhi fail kare → use rakho, recurse karo. Agar woh pass kare → discard karo, doosra simplification try karo.
Ruk jao jab koi simpler candidate fail na kare. Woh minimum report karo.
Yeh sabse simple failing case ki taraf ek greedy descent hai.
def average (xs):
return sum (xs) / len (xs)
@given (lists(integers()))
def test_average_within_bounds (xs):
avg = average(xs)
assert min (xs) <= avg <= max (xs)
Worked example Run trace karna
Yeh property kyun? Ek mean sabse chhoti aur sabse badi value ke beech honi chahiye — yeh average ki definition se hi true hai. Accha invariant hai.
Hypothesis [] (empty list) generate karta hai. len(xs)==0 → ZeroDivisionError. Bug mila!
Shrinking kyun help karta hai: empty list already minimal hai; Hypothesis xs=[] report karta hai.
Fix: contract decide karo — ek clear error raise karo, ya @given(lists(integers(), min_size=1)) require karo agar empty input genuinely invalid hai.
@given ke andar exact output assert kar dunga."
Sahi kyun lagta hai: normal tests aise hi kaam karte hain — output check karo expected ke against.
Galat kyun hai: random input ke liye tumhe expected output pata nahi hota, toh tumhe test mein function reimplement karna padega (aur uske bugs copy ho jaate hain).
Fix: ek property/relationship assert karo (round-trip, invariant), hardcoded value nahi.
Common mistake "Zyada random inputs = hamesha better, toh generators huge aur unconstrained banao."
Sahi kyun lagta hai: wider coverage safer lagti hai.
Fix: generators sirf valid inputs produce karni chahiye. Agar tumhari function ko sorted lists chahiye, toh sorted lists generate karo (assume() ya custom strategy use karo) — warna tum invalid garbage test karte ho aur false failures milti hain.
Common mistake "100 random cases pass ho gaye, toh code proven correct hai."
Sahi kyun lagta hai: bahut saare green checks.
Fix: PBT sampling hai, proof nahi. Yeh confidence badhata hai, bugs dhundta hai, lekin sab infinite inputs visit nahi kar sakta. Known tricky cases ke liye example tests ke saath combine karo.
Common mistake Hidden randomness/global state se flaky tests.
Sahi kyun lagta hai: "mere code mein random use hota hai, test bas unlucky hai."
Fix: PBT ko ek fixed input ke liye determinism chahiye. Apna code seed karo, ya property ko tolerant banao. Hypothesis khud failing seed replay karta hai taaki failures reproducible hon.
Intuition 20% jo 80% value deta hai
Teen property patterns master karo — round-trip, invariant, aur oracle — aur shrinking ka concept. Inse tum serialization, sorting, parsing, aur kisi bhi aise code ke liye PBT likh sakte ho jiske paas slow-but-correct reference ho. Yeh real use cases ka bahut bada hissa cover karta hai.
Recall Feynman: 12-saal ke bachche ko explain karo
Socho tumne ek toy machine banayi jo ek moje ko andar-bahar kar deti hai. Apne chune hue teen mojon se test karne ki jagah, tumhara robot dost ghar ke har random moje ko pakad leta hai — chhote moje, bade moje, phaate moje — aur har ek ko palat deta hai. Tum robot ko nahi batate ki result kaisa dikhna chahiye; tum use ek rule batate ho: "agar tum moja palto aur phir palto, toh exactly waisa hi dikhna chahiye jaise pehle tha." Robot saikdon mojon ko try karta hai. Jab koi rule todta hai, woh tumhe messy bada moja nahi deta — woh simpler mojon ke saath swap karta rehta hai jab tak sabse chhota moja nahi milta jo phir bhi rule todta ho, phir wahi dikhata hai. Ab tumhe exactly pata hai kya galat hai.
Property-based test kya assert karta hai, example-based test ke unlike? Ek rule/invariant jo SABHI valid inputs ke liye true hai, na ki ek single hardcoded input→output pair.
Property-based testing mein "shrinking" define karo. Failing input milne ke baad, use automatically simplify karna sabse chhote/simple input tak jo phir bhi fail kare, easy debugging ke liye.
RIIOM property patterns ke naam batao. Round-trip, Idempotence, Invariant, Oracle, Metamorphic.
Round-trip property formula do. g(f(x)) == x for all x, jahan g, f ka inverse hai (e.g. decode(encode(x))).
Idempotence property likho. f(f(x)) == f(x) (e.g. sort, normalize, dedupe).
@given ke andar exact expected output assert kyun nahi karte?Random inputs ke liye answer jaanne ke liye tumhe function reimplement karna padega, uske bugs copy ho jaate hain; uski jagah ek relationship assert karo.
"Generator/strategy" kya hota hai? Woh code jo diye gaye type ke random VALID inputs produce karta hai (integers, lists, text).
1000 random cases pass hona correctness ka proof hai? Nahi — yeh ek sampling hai jo confidence badhata hai aur bugs dhundta hai, exhaustive proof nahi.
Sort ke liye ek invariant property? len(sort(xs)) == len(xs) aur sorted output input ka ek permutation hai.
"Oracle" pattern kya hai? Fast implementation ko ek simple trusted (slow) reference ke against compare karo: f_fast(x) == f_slow(x).
PBT runs har input ke liye deterministic kyun hone chahiye? Taaki failures reproduce hon; hidden randomness/global state flaky, non-reproducible failures cause karta hai.
Unit testing — PBT example tests ko complement karta hai, replace nahi karta.
Test-driven development — properties design drive kar sakti hain.
Invariants and contracts — properties runtime-checkable invariants HAIN.
Fuzzing — random input generation ka cousin (PBT = fuzzing + structured assertions + shrinking).
Hypothesis (Python) / QuickCheck (Haskell) — original PBT tools.
Pure functions — property-test karne ki sabse aasaan cheezein (deterministic, no side effects).
feeds hundreds of inputs to
Property: rule true for all inputs
Generator: random valid inputs
Shrinking: minimal counterexample
Framework e.g. Hypothesis