ЁЯПл The SchoolтА║ЁЯУЭ TestingтА║ЁЯФ║ рдзрдбрд╛ 08 тАФ End-to-end tests: pyramid рд╡рд┐рд░реБрджреНрдз trophy
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯФ║ рдзрдбрд╛ 08 тАФ End-to-end tests: pyramid рд╡рд┐рд░реБрджреНрдз trophy

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 08 ┬╖ рдорд╛рдЧреАрд▓: lesson-07-contract-tests ┬╖ рдкреБрдвреАрд▓: lesson-09-property-based


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ07, рдЖрдгрд┐ рддреНрдпрд╛рд╢рд┐рд╡рд╛рдп end-to-end (e2e) tests тАФ рд╕рдВрдкреВрд░реНрдг system, рдмрд╛рд╣реЗрд░реВрди, рдПрдЦрд╛рджрд╛ user рдЬрд╕рд╛ рд╡рд╛рдкрд░реЗрд▓ рддрд╕рд╛ тАФ рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ team рдЬреНрдпрд╛рд╡рд░ рд╡рд╛рдж рдШрд╛рд▓рддреЗ рддреЛ рдкреНрд░рд╢реНрди: рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рдХрд╛рд░рдЪреЗ рдХрд┐рддреА tests? Test pyramid, testing trophy рдЖрдгрд┐ ice-cream cone рдпрд╛рдВрдЪреА рдПрдХрд╛ cost model рдиреЗ рддреБрд▓рдирд╛. suite_cost() рдЖрдгрд┐ LAYERS exam/lab.py рдордзреНрдпреЗ; pyramid() exam/demo.py рдордзреНрдпреЗ.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рд╢рд╛рд│рд╛ рддрдкрд╛рд╕рдгреНрдпрд╛рдЪреЗ рддреАрди рдорд╛рд░реНрдЧ рдЖрд╣реЗрдд.

рдХрддрд░рд┐рдирд╛рдиреЗ рд╕рдЧрд│реЗ рдкреВрд░реНрдг рд╢рд╛рд│реЗрдЪреНрдпрд╛ рджрд┐рд╡рд╕рд╛рдВрдиреА рддрдкрд╛рд╕рд╛рдпрдЪреЗ рдард░рд╡рд▓реЗ, рддрд░ рддрд┐рд▓рд╛ рд╢реЗрдХрдбреЛ рджрд┐рд╡рд╕ рд▓рд╛рдЧрддреАрд▓. рдкреНрд░рддреНрдпреЗрдХ run рд▓рд╛ рдкрд╛рд╡ рддрд╛рд╕ рд▓рд╛рдЧреЗрд▓, рдЖрдгрд┐ рдЬрд╡рд│рдЬрд╡рд│ рдкреНрд░рддреНрдпреЗрдХ run рдордзреНрдпреЗ рдХреБрдареЗ рдирд╛ рдХреБрдареЗ bus рдЙрд╢рд┐рд░рд╛ рдпреЗрдИрд▓рдЪ. ЁЯЪМ

рдореНрд╣рдгреВрди рддреА рдкреВрд░реНрдг рджрд┐рд╡рд╕ рдереЛрдбреЗ рдареЗрд╡рддреЗ, рдЫреЛрдЯреЗ рдкреНрд░рд╢реНрди рдЦреВрдк рдареЗрд╡рддреЗ, рдЖрдгрд┐ рдордзрд▓реНрдпрд╛ рднрд╛рдЧрд╛рд╡рд░ рджреАрдкрд┐рдХрд╛рд╢реА рд╡рд╛рдж рдШрд╛рд▓рддреЗ: рдХрддрд░рд┐рдирд╛рд▓рд╛ рдЦреВрдк unit рдкреНрд░рд╢реНрди рдЖрд╡рдбрддрд╛рдд (pyramid ЁЯФ║), рджреАрдкрд┐рдХрд╛рд▓рд╛ рдЦреВрдк integration рддрдкрд╛рд╕рдгреНрдпрд╛ рдЖрд╡рдбрддрд╛рдд (trophy ЁЯПЖ). рдЙрд▓рдЯрд╛ рдЖрдХрд╛рд░ тАФ рдмрд╣реБрддреЗрдХ рдкреВрд░реНрдг рджрд┐рд╡рд╕ тАФ рдореНрд╣рдгрдЬреЗ рдЧреЛрдВрдзрд│ рдЖрд╣реЗ рдпрд╛рд╡рд░ рджреЛрдШреАрдВрдЪреЗ рдПрдХрдордд рдЖрд╣реЗ (ice-cream cone ЁЯНж).

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart TB
    subgraph pyr["ЁЯФ║ pyramid 400 / 80 / 10"]
      p["60.0 s per run ┬╖ flaky 17%"]
    end
    subgraph tro["ЁЯПЖ trophy 150 / 300 / 10"]
      t["80.8 s per run ┬╖ flaky 23%"]
    end
    subgraph ice["ЁЯНж ice-cream cone 50 / 50 / 200"]
      i["1005.2 s per run ┬╖ flaky 87%"]
    end
    note["model: unit 0.005 s ┬╖ integration 0.1 s ┬╖ e2e 5 s per test<br/>flake chance 0.01% ┬╖ 0.05% ┬╖ 1%"]

ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрдХреГрддреА + рдПрдХ lab: https://school-edh.pages.dev/testing/lesson-diagrams.html#l08

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг suite рдЪрд╛ рд╡реЗрд│ рдЖрдгрд┐ рддреНрдпрд╛рд╡рд░рдЪрд╛ рд╡рд┐рд╢реНрд╡рд╛рд╕ рдпрд╛рд╡рд░рдЪ рд▓реЛрдХ рддреЛ рдЪрд╛рд▓рд╡рддрд╛рдд рдХреА рдирд╛рд╣реА рд╣реЗ рдард░рддреЗ. рдЬреЛ suite рдкрд╛рд╡ рддрд╛рд╕ рдШреЗрддреЛ рдЖрдгрд┐ рдмрд╣реБрддреЗрдХ runs рдордзреНрдпреЗ рд╡рд┐рдирд╛рдХрд╛рд░рдг рд▓рд╛рд▓ рд╣реЛрддреЛ, рддреЛ рд╡рдЧрд│рд▓рд╛ рдЬрд╛рддреЛ, рд╣рд┐рд░рд╡рд╛ рд╣реЛрдИрдкрд░реНрдпрдВрдд рдкреБрдиреНрд╣рд╛ рдЪрд╛рд▓рд╡рд▓рд╛ рдЬрд╛рддреЛ, рдХрд┐рдВрд╡рд╛ delete рдХреЗрд▓рд╛ рдЬрд╛рддреЛ. Pyramid рдЖрдгрд┐ trophy рдордзрд▓реНрдпрд╛ рднрд╛рдЧрд╛рд╡рд░ рдЕрд╕рд╣рдордд рдЖрд╣реЗрдд рдкрдг рдЯреЛрдХрд╛рд╡рд░ рд╕рд╣рдордд: e2e tests рдереЛрдбреЗ рдареЗрд╡рд╛, рдЖрдгрд┐ рддреЗ рдлрдХреНрдд рдкреИрд╕реЗ рдорд┐рд│рд╡реВрди рджреЗрдгрд╛рд▒реНрдпрд╛ рдХрд┐рдВрд╡рд╛ рдореЛрдбрд▓реНрдпрд╛рд╕ рдмрд╛рддрдореА рд╣реЛрдИрд▓ рдЕрд╢рд╛ рдкреНрд░рд╡рд╛рд╕рд╛рдВрд╕рд╛рдареА.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

exam/lab.py рдордзреАрд▓ LAYERS рдордзреНрдпреЗ unit, integration рдЖрдгрд┐ e2e рд╕рд╛рдареА (рдкреНрд░рддреНрдпреЗрдХ test рдЪреЗ seconds, test flake рд╣реЛрдгреНрдпрд╛рдЪреА рд╢рдХреНрдпрддрд╛) рдЖрд╣реЗ. suite_cost(counts) рд╡реЗрд│ рдмреЗрд░реАрдЬ рдХрд░рддреЗ рдЖрдгрд┐ 1 тИТ ╬а(1 тИТ p)тБ┐ рдХрд╛рдврддреЗ, рдореНрд╣рдгрдЬреЗ run рдордзреАрд▓ рдХрд┐рдорд╛рди рдПрдХ test flake рд╣реЛрдгреНрдпрд╛рдЪреА рд╢рдХреНрдпрддрд╛. exam/demo.py рдордзреАрд▓ pyramid() teaching app рдордзреВрди рдПрдХ рдЦрд░рд╛ end-to-end pass рд╕реБрджреНрдзрд╛ рдЪрд╛рд▓рд╡рддреЗ: SQLite рдордзреНрдпреЗ save рдХреЗрд▓реЗрд▓реЗ marks, fake service рдХрдбреВрди attendance, рдЖрдгрд┐ рдмрд╛рд╣реЗрд░ рдПрдХ report card.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 exam/demo.py pyramid
python3 - <<'EOF'
from exam.lab import suite_cost, LAYERS
print("model:", LAYERS)
for e2e in (0, 10, 50, 200):
    secs, flake = suite_cost({"unit": 400, "integration": 80, "e2e": e2e})
    print(f"400 unit + 80 integration + {e2e:>3} e2e тЖТ {secs:>6.1f} s ┬╖ red for no reason {flake:.1%} of runs")
secs, flake = suite_cost({"unit": 0, "integration": 0, "e2e": 1})
print(f"one e2e test alone: {secs} s, flakes {flake:.1%} ┬╖ one unit test: {suite_cost({'unit': 1})[0]} s")
EOF

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

pyramid рд╣реЗ рдЫрд╛рдкрддреЗ:

тФАтФА one end-to-end run through the teaching app: database тЖТ attendance тЖТ report card тЖТ Aishwarya: average 74.33 ┬╖ B ┬╖ attendance 86.0% ┬╖ promoted True
   pyramid         400 / 80 / 10 tests тЖТ   60.0 s per run ┬╖ a run hits at least one flaky failure 17% of the time
   trophy          150 / 300 / 10 tests тЖТ   80.8 s per run ┬╖ a run hits at least one flaky failure 23% of the time
   ice-cream cone  50 / 50 / 200 tests тЖТ 1005.2 s per run ┬╖ a run hits at least one flaky failure 87% of the time

рддреБрдордЪрд╛ snippet рд╣реЗ рдЫрд╛рдкрддреЛ:

model: {'unit': (0.005, 0.0001), 'integration': (0.1, 0.0005), 'e2e': (5.0, 0.01)}
400 unit + 80 integration +   0 e2e тЖТ   10.0 s ┬╖ red for no reason 7.7% of runs
400 unit + 80 integration +  10 e2e тЖТ   60.0 s ┬╖ red for no reason 16.5% of runs
400 unit + 80 integration +  50 e2e тЖТ  260.0 s ┬╖ red for no reason 44.2% of runs
400 unit + 80 integration + 200 e2e тЖТ 1010.0 s ┬╖ red for no reason 87.6% of runs
one e2e test alone: 5.0 s, flakes 1.0% ┬╖ one unit test: 0.005 s

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рдпрд╛ model рдордзреНрдпреЗ рдПрдХ e2e test 1000 unit tests рдЗрддрдХрд╛ рд╡реЗрд│ рдШреЗрддреЛ. e2e tests 10 рд╡рд░реВрди 200 рдХреЗрд▓реНрдпрд╛рд╡рд░ run рдЪрд╛ рд╡реЗрд│ рд╕реБрдорд╛рд░реЗ 17 рдкрдЯ рдЭрд╛рд▓рд╛ рдЖрдгрд┐ рдмрд╣реБрддреЗрдХ runs рд╡рд┐рдирд╛рдХрд╛рд░рдг рд▓рд╛рд▓ рдЭрд╛рд▓реЗ. рддрд░реАрд╣реА app рдордзреВрди рдЧреЗрд▓реЗрд▓рд╛ рддреЛ e2e pass рдПрдХрд╛ test рдЗрддрдХрд╛ рдореЛрд▓рд╛рдЪрд╛ рд╣реЛрддрд╛: database, attendance client рдЖрдгрд┐ report card рдПрдХрдореЗрдХрд╛рдВрдд рдмрд╕рддрд╛рдд рд╣реЗ рддреНрдпрд╛рдиреЗ рджрд╛рдЦрд╡рд▓реЗ (74.33 тЖТ B, 86.0% тЖТ promoted) тАФ рдЬреЗ рдХреЛрдгрддрд╛рд╣реА unit test рдПрдХрдЯреНрдпрд╛рдиреЗ рджрд╛рдЦрд╡реВ рд╢рдХрдд рдирд╛рд╣реА. рдереЛрдбреЗ, рдкрдг рд╢реВрдиреНрдп рдирд╛рд╣реА.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд

рдЦрд▒реНрдпрд╛ project рдордзреНрдпреЗ тАФ Python рд╕рд╛рдареА Playwright рд╡рд╛рдкрд░реВрди рдПрдХ e2e test (pip install pytest-playwright, рдордЧ playwright install chromium). Playwright elements рддрдпрд╛рд░ рд╣реЛрдИрдкрд░реНрдпрдВрдд рдЖрдкреЛрдЖрдк рд╡рд╛рдЯ рдкрд╛рд╣рддреЗ, рддреНрдпрд╛рдореБрд│реЗ рдмрд░реЗрдЪ flaky sleeps рдирд┐рдШреВрди рдЬрд╛рддрд╛рдд:

from playwright.sync_api import Page, expect

def test_report_card_page(page: Page):
    page.goto("https://staging.school.example/report/42")
    expect(page.get_by_role("heading", name="Report card")).to_be_visible()
    expect(page.get_by_test_id("grade")).to_have_text("B")
pytest tests/e2e --browser chromium --tracing retain-on-failure   # keep a trace to replay failures

JavaScript runner рдореНрд╣рдгрдЬреЗ npx playwright test; Cypress рдЖрдгрд┐ Selenium рд╣реЗ рдЗрддрд░ рдиреЗрд╣рдореАрдЪреЗ рдкрд░реНрдпрд╛рдп рдЖрд╣реЗрдд. рддреБрдордЪреЗ рдЦрд░реЗ рдерд░ рдореЛрдЬрд╛:

pytest --durations=10              # the 10 slowest tests

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рддреБрдордЪреЗ 5тАУ10 рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ user рдкреНрд░рд╡рд╛рд╕ рд▓рд┐рд╣реВрди рдХрд╛рдврд╛ (log in, marks submit рдХрд░рдгреЗ, report cards publish рдХрд░рдгреЗ) рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХрд╛рд▓рд╛ рдПрдХ e2e test рджреНрдпрд╛. рдмрд╛рдХреА рд╕рдЧрд│реЗ pyramid рдордзреНрдпреЗ рдЦрд╛рд▓рдЪреНрдпрд╛ рдерд░рд╛рдд тАФ рдЖрдгрд┐ suite рдЪрд╛ run рд╡реЗрд│ рд╡ flake rate рдЗрддрд░ рдХреЛрдгрддреНрдпрд╛рд╣реА production metric рд╕рд╛рд░рдЦрд╛ track рдХрд░рд╛.

тПня╕П рдкреБрдвреЗ

рдЖрддреНрддрд╛рдкрд░реНрдпрдВрдд рдХрддрд░рд┐рдирд╛ рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рд╢реНрди рд╕реНрд╡рддрдГ рдирд┐рд╡рдбрддреЗ. рд╕рдВрдЧрдгрдХрд╛рдиреЗрдЪ рдПрдХрд╛ рдирд┐рдпрдорд╛рд╡рд░реВрди рд╢реЗрдХрдбреЛ рдкреНрд░рд╢реНрди рддрдпрд╛рд░ рдХреЗрд▓реЗ тАФ рдЖрдгрд┐ рдордЧ failure рд╢рдХреНрдп рддрд┐рддрдХрд╛ рд▓рд╣рд╛рди рдХреЗрд▓рд╛ рддрд░? Property-based testing.

git checkout lesson-09-property-based

ЁЯФ║ Lesson 08 тАФ End-to-end tests: the pyramid vs the trophy

ЁЯУН You are here: Lesson 08 of 12 ┬╖ Previous: lesson-07-contract-tests ┬╖ Next: lesson-09-property-based


ЁЯУж What's in this branch

Lessons 01тАУ07, plus end-to-end (e2e) tests тАФ the whole system, from the outside, as a user would use it тАФ and the question every team argues about: how many tests of each kind? The test pyramid, the testing trophy and the ice-cream cone, compared with a cost model. suite_cost() and LAYERS in exam/lab.py; pyramid() in exam/demo.py.

ЁЯзТ Explain like I'm 5

There are three ways to check the school.

If Katrina tried to test everything with full school days, she would need hundreds of them. Every run would take a quarter of an hour, and almost every run would have a late bus somewhere. ЁЯЪМ

So she keeps few full days, many small questions, and argues with Dipika about the middle: Katrina likes lots of unit questions (the pyramid ЁЯФ║), Dipika likes lots of integration checks (the trophy ЁЯПЖ). Both agree the upside-down shape тАФ mostly full days тАФ is a mess (the ice-cream cone ЁЯНж).

ЁЯЧ║я╕П Diagram

flowchart TB
    subgraph pyr["ЁЯФ║ pyramid 400 / 80 / 10"]
      p["60.0 s per run ┬╖ flaky 17%"]
    end
    subgraph tro["ЁЯПЖ trophy 150 / 300 / 10"]
      t["80.8 s per run ┬╖ flaky 23%"]
    end
    subgraph ice["ЁЯНж ice-cream cone 50 / 50 / 200"]
      i["1005.2 s per run ┬╖ flaky 87%"]
    end
    note["model: unit 0.005 s ┬╖ integration 0.1 s ┬╖ e2e 5 s per test<br/>flake chance 0.01% ┬╖ 0.05% ┬╖ 1%"]

ЁЯЧ║я╕П Drawn version + a lab: https://school-edh.pages.dev/testing/lesson-diagrams.html#l08

тЭУ What

ЁЯдФ Why

Because the time and trust of a suite decide whether people run it. A suite that takes a quarter of an hour and goes red for no reason in most runs gets skipped, rerun until green, or deleted. The pyramid and the trophy disagree on the middle but agree on the top: keep e2e tests few, covering the journeys that make money or would make the news if they broke.

ЁЯФз How (in this repo)

LAYERS in exam/lab.py holds (seconds per test, chance a test flakes) for unit, integration and e2e. suite_cost(counts) adds up the time and computes 1 тИТ ╬а(1 тИТ p)тБ┐, the chance that at least one test in the run flakes. pyramid() in exam/demo.py also runs one real end-to-end pass through the teaching app: marks saved in SQLite, attendance from the fake service, a report card out.

ЁЯзк Try it

python3 exam/demo.py pyramid
python3 - <<'EOF'
from exam.lab import suite_cost, LAYERS
print("model:", LAYERS)
for e2e in (0, 10, 50, 200):
    secs, flake = suite_cost({"unit": 400, "integration": 80, "e2e": e2e})
    print(f"400 unit + 80 integration + {e2e:>3} e2e тЖТ {secs:>6.1f} s ┬╖ red for no reason {flake:.1%} of runs")
secs, flake = suite_cost({"unit": 0, "integration": 0, "e2e": 1})
print(f"one e2e test alone: {secs} s, flakes {flake:.1%} ┬╖ one unit test: {suite_cost({'unit': 1})[0]} s")
EOF

тЬЕ Verify тАФ what you should see

pyramid prints:

тФАтФА one end-to-end run through the teaching app: database тЖТ attendance тЖТ report card тЖТ Aishwarya: average 74.33 ┬╖ B ┬╖ attendance 86.0% ┬╖ promoted True
   pyramid         400 / 80 / 10 tests тЖТ   60.0 s per run ┬╖ a run hits at least one flaky failure 17% of the time
   trophy          150 / 300 / 10 tests тЖТ   80.8 s per run ┬╖ a run hits at least one flaky failure 23% of the time
   ice-cream cone  50 / 50 / 200 tests тЖТ 1005.2 s per run ┬╖ a run hits at least one flaky failure 87% of the time

Your snippet prints:

model: {'unit': (0.005, 0.0001), 'integration': (0.1, 0.0005), 'e2e': (5.0, 0.01)}
400 unit + 80 integration +   0 e2e тЖТ   10.0 s ┬╖ red for no reason 7.7% of runs
400 unit + 80 integration +  10 e2e тЖТ   60.0 s ┬╖ red for no reason 16.5% of runs
400 unit + 80 integration +  50 e2e тЖТ  260.0 s ┬╖ red for no reason 44.2% of runs
400 unit + 80 integration + 200 e2e тЖТ 1010.0 s ┬╖ red for no reason 87.6% of runs
one e2e test alone: 5.0 s, flakes 1.0% ┬╖ one unit test: 0.005 s

ЁЯПБ What you just proved

In this model, one e2e test costs as much time as 1000 unit tests. Going from 10 to 200 e2e tests multiplied the run time by about 17 and made most runs red for no reason. The e2e pass through the app was still worth one test: it showed that the database, the attendance client and the report card fit together (74.33 тЖТ B, 86.0% тЖТ promoted) тАФ which no unit test alone can show. Few, not none.

тЪая╕П Common mistakes

ЁЯПн In production

On a real project тАФ an e2e test with Playwright for Python (pip install pytest-playwright, then playwright install chromium). Playwright waits automatically for elements to be ready, which removes many flaky sleeps:

from playwright.sync_api import Page, expect

def test_report_card_page(page: Page):
    page.goto("https://staging.school.example/report/42")
    expect(page.get_by_role("heading", name="Report card")).to_be_visible()
    expect(page.get_by_test_id("grade")).to_have_text("B")
pytest tests/e2e --browser chromium --tracing retain-on-failure   # keep a trace to replay failures

The JavaScript runner is npx playwright test; Cypress and Selenium are the other common choices. Measure your real layers:

pytest --durations=10              # the 10 slowest tests

ЁЯПн Why this matters in production: write down your 5тАУ10 critical user journeys (log in, submit marks, publish report cards) and give each one e2e test. Everything else goes lower in the pyramid тАФ and track the suite's run time and flake rate like any other production metric.

тПня╕П Next

So far Katrina chooses every question herself. What if the computer invented hundreds of questions from a rule тАФ and then made a failure as small as possible? Property-based testing.

git checkout lesson-09-property-based
тЖР Previouscontract testsNext тЖТproperty based

This page is the lesson's README from the lesson-08-e2e-pyramid branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.