ЁЯО▓ рдзрдбрд╛ 09 тАФ Property-based testing: рдПрдХ рдирд┐рдпрдо, рдПрдХ seed рдЖрдгрд┐ shrinking
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 09 ┬╖ рдорд╛рдЧреАрд▓: lesson-08-e2e-pyramid ┬╖ рдкреБрдвреАрд▓: lesson-10-coverage-mutation
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбреЗ 01тАУ08, рдЖрдгрд┐ рддреНрдпрд╛рд╢рд┐рд╡рд╛рдп property-based testing: рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рд╢реНрди рд╕реНрд╡рддрдГ рдирд┐рд╡рдбрдгреНрдпрд╛рдРрд╡рдЬреА рддреБрдореНрд╣реА
рдкреНрд░рддреНрдпреЗрдХ input рд╕рд╛рдареА рдЦрд░рд╛ рдЕрд╕рд╛рдпрд▓рд╛рдЪ рд╣рд╡рд╛ рдЕрд╕рд╛ рдирд┐рдпрдо (рдПрдХ property) рд╕рд╛рдВрдЧрддрд╛, рдПрдХрд╛ seeded generator
рд▓рд╛ рд╢рдВрднрд░ inputs рддрдпрд╛рд░ рдХрд░реВ рджреЗрддрд╛, рдЖрдгрд┐ рдПрдЦрд╛рджрд╛ fail рдЭрд╛рд▓рд╛ рдХреА рддреНрдпрд╛рд▓рд╛ рдЕрдЬреВрди fail рд╣реЛрдгрд╛рд▒реНрдпрд╛ рд╕реЛрдкреНрдпрд╛ input рдкрд░реНрдпрдВрдд
shrink рдХрд░рддрд╛. Rng, forall() рдЖрдгрд┐ shrink() exam/lab.py рдордзреНрдпреЗ; property
exam/demo.py рдордзреНрдпреЗ.
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
рдПрдХреЗрдХ рдкреНрд░рд╢реНрди рд▓рд┐рд╣реВрди рдХрддрд░рд┐рдирд╛ рдХрдВрдЯрд╛рд│рд▓реА рдЖрд╣реЗ. рдореНрд╣рдгреВрди рддреА рддреНрдпрд╛рдРрд╡рдЬреА рдПрдХ рдирд┐рдпрдо рд▓рд┐рд╣реВрди рдареЗрд╡рддреЗ:
".5 рдиреЗ рд╕рдВрдкрдгрд╛рд▒реНрдпрд╛ рдЧреБрдгрд╛рд▓рд╛ рдкреБрдврдЪреНрдпрд╛ рдкреВрд░реНрдг рд╕рдВрдЦреНрдпреЗрдЗрддрдХрд╛рдЪ рддреЛрдЪ grade рдорд┐рд│рд╛рдпрд▓рд╛ рд╣рд╡рд╛." (рдХрд╛рд░рдг рдЕрд░реНрдзреЗ рдЧреБрдг рд╡рд░ round рд╣реЛрддрд╛рдд.)
рдордЧ рддреА рддреЛ рдирд┐рдпрдо рдПрдХрд╛ рдкреНрд░рд╢реНрди-рдпрдВрддреНрд░рд╛рдХрдбреЗ ЁЯО▓ рджреЗрддреЗ. рдпрдВрддреНрд░ рдПрдХ рдкреВрд░реНрдг рд╕рдВрдЦреНрдпрд╛ n рдирд┐рд╡рдбрддреЗ тАФ 0 рддреЗ 99 рдордзрд▓реА
рдХреЛрдгрддреАрд╣реА тАФ рдЖрдгрд┐ рддрдкрд╛рд╕рддреЗ: n + 0.5 рд▓рд╛ n + 1 рдЗрддрдХрд╛рдЪ grade рдорд┐рд│рддреЛ рдХрд╛? рд╣реЗ рддреЗ 100 рд╡реЗрд│рд╛ рдХрд░рддреЗ.
9рд╡реНрдпрд╛ рдкреНрд░рдпрддреНрдирд╛рдд рддреЗ 74 рдирд┐рд╡рдбрддреЗ: 74.5 рд▓рд╛ B рдорд┐рд│рддреЛ, рдкрдг 75 рд▓рд╛ A. рдирд┐рдпрдо рдореЛрдбрд▓рд╛! ЁЯЪи
рдЖрддрд╛ рдпрдВрддреНрд░ рдПрдХ рд╣реБрд╢рд╛рд░ рдЧреЛрд╖реНрдЯ рдХрд░рддреЗ. рддреЗ рд╡рд┐рдЪрд╛рд░рддреЗ: "рдирд┐рдпрдо рдореЛрдбрдгрд╛рд░реА рдЕрдЬреВрди рд╕реЛрдкреА рд╕рдВрдЦреНрдпрд╛ рдЖрд╣реЗ рдХрд╛?" рддреЗ 0 рд╡рд╛рдкрд░реВрди рдкрд╛рд╣рддреЗ, рдордЧ 74 рдЪрд╛ рдЕрд░реНрдзрд╛, рдордЧ 64, 54, 44 тАФ рддреАрд╣реА рдореЛрдбрддреЗ! рдордЧ 44 рдкрд╛рд╕реВрди: 0, 22, 34 тАФ рддреАрд╣реА рдореЛрдбрддреЗ! 34 рдкрд╛рд╕реВрди рддреНрдпрд╛рд╣реВрди рд▓рд╣рд╛рди рдХреЛрдгрддреАрдЪ рд╕рдВрдЦреНрдпрд╛ рдирд┐рдпрдо рдореЛрдбрдд рдирд╛рд╣реА. рдореНрд╣рдгреВрди рдпрдВрддреНрд░ рддреНрдпрд╛рд▓рд╛ рд╕рд╛рдкрдбрд▓реЗрд▓реЗ рд╕рд░реНрд╡рд╛рдд рд╕реЛрдкреЗ failure рд╕рд╛рдВрдЧрддреЗ: 34.5. рдзрдбрд╛ 03 рдордзрд▓рд╛рдЪ bug, рдХрддрд░рд┐рдирд╛рдиреЗ рдПрдХрд╣реА score рди рдирд┐рд╡рдбрддрд╛ рд╕рд╛рдкрдбрд▓реЗрд▓рд╛.
рдпрдВрддреНрд░ рдЖрдкрд▓рд╛ seed тАФ рддреНрдпрд╛рдЪреНрдпрд╛ рдлрд╛рд╢рд╛рдВрдЪрд╛ рд╕реБрд░реБрд╡рд╛рддреАрдЪрд╛ рдЖрдХрдбрд╛ тАФ рд╕реБрджреНрдзрд╛ рд▓рд┐рд╣реВрди рдареЗрд╡рддреЗ, рдореНрд╣рдгрдЬреЗ рдХреЛрдгрд╛рд▓рд╛рд╣реА рдиреЗрдордХреЗ рддреЗрдЪ 100 рдкреНрд░рд╢реНрди рдкреБрдиреНрд╣рд╛ рдЪрд╛рд▓рд╡рддрд╛ рдпреЗрддрд╛рдд.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
r["ЁЯУЬ property<br/>grade(n + 0.5) == grade(n + 1)"] --> g["ЁЯО▓ seeded generator<br/>seed 3, n in 0..99"]
g --> run["run 1..100"]
run -->|"run 9: n = 74"| f["тЭМ 74.5 тЖТ B but 75 тЖТ A"]
f --> s["ЁЯФН shrink<br/>74 тЖТ 44 тЖТ 34"]
s --> m["smallest failing score 34.5"]
ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрдХреГрддреА + рдПрдХ lab: https://school-edh.pages.dev/testing/lesson-diagrams.html#l09
тЭУ рдХрд╛рдп
- Example-based test тАФ input рдЖрдгрд┐ рдЕрдкреЗрдХреНрд╖рд┐рдд output рддреБрдореНрд╣реА рдирд┐рд╡рдбрддрд╛ (рдзрдбреЗ 02тАУ03).
- Property тАФ рд╕рд░реНрд╡ рд╡реИрдз inputs рд╕рд╛рдареА рдЦрд░реЗ рдЕрд╕рдгрд╛рд░реЗ рд╡рд┐рдзрд╛рди. рдиреЗрд╣рдореАрдЪреЗ рдкреНрд░рдХрд╛рд░:
- invariants тАФ "average рд╕рд░реНрд╡рд╛рдд рдХрдореА рдЖрдгрд┐ рд╕рд░реНрд╡рд╛рдд рдЬрд╛рд╕реНрдд mark рдпрд╛рдВрдЪреНрдпрд╛рдордзреНрдпреЗрдЪ рдЕрд╕рддреЗ"
- inputs рдордзреАрд▓ рд╕рдВрдмрдВрдз (metamorphic) тАФ "x.5 рд▓рд╛ x + 1 рдЗрддрдХрд╛рдЪ grade рдорд┐рд│рддреЛ"; "рдЬрд╛рд╕реНрдд score рд▓рд╛ рдХрдзреАрдЪ рдХрдореА grade рдорд┐рд│рдд рдирд╛рд╣реА"
- round trips тАФ
decode(encode(x)) == x - oracle тАФ рдПрдХрд╛ рд╕рд╛рдзреНрдпрд╛, рд╣рд│реВ, рдЙрдШрдбрдкрдгреЗ рдмрд░реЛрдмрд░ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ version рд╢реА рддреБрд▓рдирд╛ рдХрд░рдгреЗ
- Generator (strategy) тАФ random рд╡реИрдз inputs рддрдпрд╛рд░ рдХрд░рддреЛ. рдЪрд╛рдВрдЧрд▓реЗ generators рдХрдбреЗрдЪреНрдпрд╛ values (0, рд░рд┐рдХрд╛рдореЗ, рдХрдорд╛рд▓) рдирд┐рд╡реНрд╡рд│ рдпреЛрдЧрд╛рдпреЛрдЧрд╛рдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд рд╡реЗрд│рд╛ рджреЗрддрд╛рдд.
- Seed тАФ pseudo-random generator рдЪреА рд╕реБрд░реБрд╡рд╛рддреАрдЪреА value. рддреЛрдЪ seed, рддреЗрдЪ inputs: failure рдкреБрдиреНрд╣рд╛ рдЪрд╛рд▓рд╡рддрд╛ рдпреЗрддреЗ.
- Shrinking тАФ failure рдирдВрддрд░ рд╕реЛрдкреЗ inputs (рд▓рд╣рд╛рди рд╕рдВрдЦреНрдпрд╛, рдЫреЛрдЯреНрдпрд╛ lists) рд╡рд╛рдкрд░реВрди рдкрд╛рд╣рдгреЗ рдЖрдгрд┐ рдЬреЗ рдЕрдЬреВрдирд╣реА fail рд╣реЛрддрд╛рдд рддреЗ рдареЗрд╡рдгреЗ. рдирд┐рдХрд╛рд▓ рдореНрд╣рдгрдЬреЗ рд╕реНрдерд╛рдирд┐рдХ рдкрд╛рддрд│реАрд╡рд░ рд╕рд░реНрд╡рд╛рдд рд╕реЛрдкрд╛ failing case тАФ random input рдкреЗрдХреНрд╖рд╛ debug рдХрд░рд╛рдпрд▓рд╛ рд╕рд╣рд╕рд╛ рдЦреВрдкрдЪ рд╕реЛрдкрд╛. рддреЛ рд╢рдХреНрдп рддрд┐рддрдХрд╛ рд╕рд░реНрд╡рд╛рдд рд▓рд╣рд╛рди рдЕрд╕реЗрд▓рдЪ рдпрд╛рдЪреА рдЦрд╛рддреНрд░реА рдирд╛рд╣реА.
- QuickCheck тАФ рдореВрд│ property-based testing tool (Koen Claessen рдЖрдгрд┐ John Hughes, Haskell, 2000). Python рд╕рд╛рдареА Hypothesis рдЖрд╣реЗ.
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг рддреБрдореНрд╣рд╛рд▓рд╛ рд╕реБрдЪрдгрд╛рд░реЗ рдкреНрд░рд╢реНрди рдХрд╛рдп рдЪреБрдХреВ рд╢рдХрддреЗ рдпрд╛рдЪреНрдпрд╛ рддреБрдордЪреНрдпрд╛ рдХрд▓реНрдкрдиреЗрдкреБрд░рддреЗрдЪ рдорд░реНрдпрд╛рджрд┐рдд рдЕрд╕рддрд╛рдд. Property рдирд┐рдпрдо рдПрдХрджрд╛ рд╕рд╛рдВрдЧрддреЗ, рдЖрдгрд┐ рдпрдВрддреНрд░ рддреЛ рдореЛрдбрдгрд╛рд░реЗ inputs рд╢реЛрдзрддреЗ тАФ рдЬреНрдпрд╛рдд рдХреЛрдгреАрд╣реА рд╣рд╛рддрд╛рдиреЗ рдирд┐рд╡рдбрдгрд╛рд░ рдирд╛рд╣реА рдЕрд╕реЗ inputs рд╕реБрджреНрдзрд╛ рдЕрд╕рддрд╛рдд. рд╕реНрдкрд╖реНрдЯ рдирд┐рдпрдо рдЖрдгрд┐ рдкреНрд░рдЪрдВрдб input space рдЕрд╕рд▓реЗрд▓реНрдпрд╛ code рд╡рд░ рддреЗ рд╕рд░реНрд╡рд╛рдд рдкреНрд░рднрд╛рд╡реА рдЕрд╕рддреЗ: parsers, serializers, рдкреИрд╕реЗ рдЖрдгрд┐ rounding, sorting, рддрд╛рд░рдЦрд╛рдВрдЪреЗ рдЧрдгрд┐рдд.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
exam/lab.py рдордзреАрд▓ Rng(seed) рд╣рд╛ рдПрдХ рдЫреЛрдЯрд╛ linear congruential generator рдЖрд╣реЗ;
rng.int(lo, hi) lo рддреЗ hi рдордзрд▓реА рдкреВрд░реНрдг рд╕рдВрдЦреНрдпрд╛ рджреЗрддреЛ. forall(prop, gen, runs, seed) gen(rng)
рд╡рд╛рдкрд░реВрди runs inputs рдХрд╛рдврддреЗ; рдЬреНрдпрд╛ рдкрд╣рд┐рд▓реНрдпрд╛ input рд╡рд░ prop(x) false рдпреЗрддреЗ, рддрд┐рдереЗ рддреЗ shrink(prop, x)
рдмреЛрд▓рд╡рддреЗ рдЖрдгрд┐ run рдХреНрд░рдорд╛рдВрдХ, failing input, shrink рдХреЗрд▓реЗрд▓рд╛ input рдЖрдгрд┐ рдкрд╛рдпрд▒реНрдпрд╛ рдкрд░рдд рджреЗрддреЗ. shrink рдЖрдзреА 0,
n // 2, рдордЧ n тИТ 10, n тИТ 20, тАж, рдЖрдгрд┐ n тИТ 1 рд╡рд╛рдкрд░реВрди рдкрд╛рд╣рддреЗ, рдЕрдЬреВрдирд╣реА fail рд╣реЛрдгрд╛рд░рд╛ рдкрд╣рд┐рд▓рд╛ рдЙрдореЗрджрд╡рд╛рд░
рдареЗрд╡рддреЗ, рдЖрдгрд┐ рдХреЛрдгрддрд╛рдЪ fail рд╣реЛрдд рдирд╛рд╣реА рддреЛрдкрд░реНрдпрдВрдд рд╣реЗ рдкреБрдиреНрд╣рд╛ рдкреБрдиреНрд╣рд╛ рдХрд░рддреЗ.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
python3 exam/demo.py property
python3 - <<'EOF'
from exam.lab import forall, shrink, Rng
from exam.grades import grade, grade_v1, report_card
prop = lambda n: grade_v1(n + 0.5) == grade_v1(n + 1)
for seed in (1, 2, 3, 6, 9):
i, x, small, steps = forall(prop, lambda r: r.int(0, 99), runs=100, seed=seed)
print(f"seed {seed}: first failure on run {i:>2} at n={x} ┬╖ shrink path {steps}")
print("shrink from 74 by hand:", shrink(prop, 74))
avg_between = lambda rng: (lambda ms: min(ms) <= report_card("x", dict(enumerate(ms)), 90)["average"] <= max(ms))([rng.int(0, 100) for _ in range(rng.int(1, 6))])
i, x, _, _ = forall(lambda seed: avg_between(Rng(seed)), lambda r: r.int(1, 10**6), runs=200, seed=5)
print(f"'the average lies between the lowest and highest mark': {i} runs, failures {x}")
EOF
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
property рд╣реЗ рдЫрд╛рдкрддреЗ:
grade_v1, seed 1: fails on run 25 at n=34 (34.5) ┬╖ shrinks 34 тЖТ smallest failing score 34.5
grade_v1, seed 3: fails on run 9 at n=74 (74.5) ┬╖ shrinks 74 тЖТ 44 тЖТ 34 тЖТ smallest failing score 34.5
grade (fixed), seed 1: 100 runs, failures: None
grade_v1 property 'a higher score never gets a lower grade', 300 runs тЖТ failures: None
рддреБрдордЪрд╛ snippet рд╣реЗ рдЫрд╛рдкрддреЛ:
seed 1: first failure on run 25 at n=34 ┬╖ shrink path [34]
seed 2: first failure on run 47 at n=34 ┬╖ shrink path [34]
seed 3: first failure on run 9 at n=74 ┬╖ shrink path [74, 44, 34]
seed 6: first failure on run 6 at n=44 ┬╖ shrink path [44, 34]
seed 9: first failure on run 2 at n=34 ┬╖ shrink path [34]
shrink from 74 by hand: (34, [74, 44, 34])
'the average lies between the lowest and highest mark': 200 runs, failures None
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
рдкрд╛рдЪ рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ seeds рдирд╛ bug рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ runs рдордзреНрдпреЗ рдЖрдгрд┐ рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ scores рд╡рд░ рд╕рд╛рдкрдбрд▓рд╛, рдЖрдгрд┐
shrinking рдиреЗ рддреНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХрд╛рд▓рд╛ рдПрдХрд╛рдЪ рд╕реЛрдкреНрдпрд╛ case рдкрд░реНрдпрдВрдд рдЖрдгрд▓реЗ, 34.5. рджреБрд░реБрд╕реНрдд grade 100 runs
pass рдЭрд╛рд▓реЗ. рдкрдг demo рдордзрд▓реА рджреБрд╕рд░реА property рдкрд╛рд╣рд╛: "рдЬрд╛рд╕реНрдд score рд▓рд╛ рдХрдзреАрдЪ рдХрдореА grade рдорд┐рд│рдд рдирд╛рд╣реА" рд╣реА
bug рдЕрд╕рд▓реЗрд▓реНрдпрд╛ grade_v1 рд╕рд╛рдареАрд╣реА pass рдЭрд╛рд▓реА тАФ score рд╡рд╛рдврд▓реНрдпрд╛рд╡рд░ round() рдХрдзреАрдЪ рдЦрд╛рд▓реА рдЬрд╛рдд рдирд╛рд╣реА.
Property рдлрдХреНрдд рддреЗрдЪ рд╢реЛрдзрддреЗ рдЬреЗ рддреА рд╕рд╛рдВрдЧрддреЗ. рдЖрдгрд┐ 100 pass рдЭрд╛рд▓реЗрд▓реЗ runs рдореНрд╣рдгрдЬреЗ 100 рдЙрджрд╛рд╣рд░рдгреЗ, рд╕рд┐рджреНрдзрддрд╛
рдирд╡реНрд╣реЗ; рджреБрд░реНрджреИрд╡реА seed рдЖрдгрд┐ рдереЛрдбреЗ runs рдЕрд╕рддреАрд▓ рддрд░ bug рд▓рдкреВрди рд░рд╛рд╣реВ рд╢рдХрддреЛ.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
- test рд╣реЛрдгрд╛рд░рд╛ code рдкреБрдиреНрд╣рд╛ рддрд╕рд╛рдЪ рд▓рд┐рд╣рд┐рдгрд╛рд▒реНрдпрд╛ properties (рддреНрдпрд╛рдВрдЪреНрдпрд╛рдд рддреНрдпрд╛рдЪреЗрдЪ bugs рдпреЗрддрд╛рдд)
- failure report рдордзреНрдпреЗ seed рдирд╕рдгреЗ, рддреНрдпрд╛рдореБрд│реЗ failure рдкреБрдиреНрд╣рд╛ рдЪрд╛рд▓рд╡рддрд╛ рдпреЗрдд рдирд╛рд╣реА
- рдХрдбреЗрдЪреНрдпрд╛ values (рд░рд┐рдХрд╛рдореНрдпрд╛ lists, 0, рдЕрд░реНрдзреЗ рдЧреБрдг, рдЦреВрдк рдореЛрдареНрдпрд╛ values) рдХрдзреАрдЪ рди рджреЗрдгрд╛рд░реЗ generators
- рд╕рд╛рдкрдбрд▓реЗрд▓реЗ failure fixed example test рдореНрд╣рдгреВрди рдЬреЛрдбрдгреНрдпрд╛рдРрд╡рдЬреА рдлреЗрдХреВрди рджреЗрдгреЗ
- "100 runs pass рдЭрд╛рд▓реЗ" рдореНрд╣рдгрдЬреЗ "рдмрд░реЛрдмрд░ рдЕрд╕рд▓реНрдпрд╛рдЪреЗ рд╕рд┐рджреНрдз рдЭрд╛рд▓реЗ" рдЕрд╕реЗ рд╕рдордЬрдгреЗ
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд
рдЦрд▒реНрдпрд╛ project рдордзреНрдпреЗ тАФ рддреАрдЪ property Hypothesis рдордзреНрдпреЗ (pip install hypothesis), pytest
рдиреЗ рдЪрд╛рд▓рд╡рд▓реЗрд▓реА:
from hypothesis import given, example, strategies as st
from exam.grades import grade
@given(st.integers(min_value=0, max_value=99))
@example(34) # always also try the case that bit us once
def test_half_marks_round_up(n):
assert grade(n + 0.5) == grade(n + 1)
grade_v1 рд╡рд░ рдЪрд╛рд▓рд╡рд▓реНрдпрд╛рд╡рд░ Hypothesis shrink рдХреЗрд▓реЗрд▓рд╛ input рд╕рд╛рдВрдЧрддреЗ:
Falsifying example: test_half_marks_round_up(
n=34,
)
Hypothesis failing examples рд╕реНрдерд╛рдирд┐рдХ .hypothesis/ directory рдордзреНрдпреЗ рд╕рд╛рдард╡рддреЗ рдЖрдгрд┐ рдкреБрдврдЪреНрдпрд╛ run рдордзреНрдпреЗ
рддреЗ рдЖрдзреА рд╡рд╛рдкрд░реВрди рдкрд╛рд╣рддреЗ; @settings(max_examples=500) рдЬрд╛рд╕реНрдд examples рдорд╛рдЧрддреЗ, рдЖрдгрд┐ @seed(1234)
randomness рдкрдХреНрдХреА рдХрд░рддреЗ. рдЗрддрд░ ecosystems: QuickCheck (Haskell), fast-check
(JavaScript/TypeScript), jqwik (Java), proptest (Rust).
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рдЕрд╕реЗ рдПрдХ function рд╢реЛрдзрд╛ рдЬреНрдпрд╛рдЪрд╛ рдирд┐рдпрдо рддреБрдореНрд╣реА рдПрдХрд╛ рд╡рд╛рдХреНрдпрд╛рдд рд╕рд╛рдВрдЧреВ рд╢рдХрддрд╛ тАФ serializer (round trip), рдХрд┐рдВрдорддреАрдЪреЗ рдЧрдгрд┐рдд (рдХрдзреАрдЪ negative рдирд╛рд╣реА), sort (output рдХреНрд░рдорд╛рдиреЗ рдЖрд╣реЗ рдЖрдгрд┐ рддреНрдпрд╛рдд рддреЗрдЪ items рдЖрд╣реЗрдд). рддреА property рдЖрдзреА рд▓рд┐рд╣рд╛; рд╣рд╛рддрд╛рдиреЗ рдирд┐рд╡рдбрд▓реЗрд▓реА рдЙрджрд╛рд╣рд░рдгреЗрд╣реА рдареЗрд╡рд╛.
тПня╕П рдкреБрдвреЗ
Test suite рдХрд┐рддреА рдЪрд╛рдВрдЧрд▓рд╛ рдЖрд╣реЗ? "рдХреЛрдгрддреНрдпрд╛ lines рдЪрд╛рд▓рд▓реНрдпрд╛?" рд╣реЗ рдиреЗрд╣рдореАрдЪреЗ рдЙрддреНрддрд░ тАФ coverage. Mutation testing рдПрдХ рдХрдареАрдг рдкреНрд░рд╢реНрди рд╡рд┐рдЪрд╛рд░рддреЗ: code рдЪреБрдХреАрдЪрд╛ рдЕрд╕рддрд╛ рддрд░ рдХреЛрдгрддреНрдпрд╛ test рд▓рд╛ рддреЗ рд▓рдХреНрд╖рд╛рдд рдЖрд▓реЗ рдЕрд╕рддреЗ рдХрд╛?
git checkout lesson-10-coverage-mutation