📝 शाळेच्या पद्धतीने software testing शिका

code जे करायला हवे तेच करतो हे कसे तपासायचे, हे शाळेच्या परीक्षा हॉलच्या रूपात शिकवले आहे: test म्हणजे परीक्षेचे प्रश्न, fixture म्हणजे तयार ठेवलेल्या उत्तरपत्रिका, test double म्हणजे बदली परीक्षक, आणि flaky test म्हणजे भिंतीवरचे डुगडुगणारे घड्याळ. प्रत्येक धडा ही शाळेतील एक गोष्ट आहे, सोबत एक काढलेली आकृती आणि एक lab — आणि परीक्षा हॉल repo मध्येच आहे: शोधण्यासाठी खरे bug असलेले grading module (exam/grades.py) आणि testing tools शुद्ध Python मधील deterministic models म्हणून (exam/lab.py, शून्य dependencies).

💸 test का करायचे✏️ arrange · act · assert🎯 boundary values🗂️ fixtures🧑‍🏫 stubs · fakes · mocks🔌 खरे SQLite🤝 contracts🔺 pyramid · trophy🎲 properties🧬 mutants🕰️ flaky tests🚦 TDD · CI

📝 भाग 1 — मूलभूत गोष्टी (1–4)

  • मुळात प्रश्न का विचारायचे 💸
  • एक प्रश्न, नीट विचारलेला ✏️
  • योग्य प्रश्न 🎯
  • तयार उत्तरपत्रिका 🗂️

🧑‍🏫 भाग 2 — एका unit च्या पलीकडे (5–8)

  • बदली परीक्षक 🧑‍🏫
  • खरी गुणांची नोंदवही 🔌
  • दोन कार्यालयांमधील वचन 🤝
  • शाळेचा पूर्ण दिवस 🔺

🔬 भाग 3 — tests किती चांगले आहेत (9–12)

  • प्रश्न बनवणारे यंत्र 🎲
  • मुद्दाम पेरलेल्या चुका 🧬
  • डुगडुगणारे घड्याळ 🕰️
  • प्रत्येक बदलावर प्रश्न 🚦
# the 60-second wow — one grading module, twelve ways to test it:
git clone https://github.com/BaluRaut/learn-testing-school.git && cd learn-testing-school
python3 exam/demo.py               # 12 lessons: a rounding bug, a lying stub, 10 mutants, a wobbly clock
python3 exam/test_exam.py          # 12 checks across the lessons

तुम्हाला काय दिसायला हवे (छाटलेले — यश असे दिसते):

═══ why ═══
── Dipika's grade_v1 sits 5 easy exam questions: 5/5 correct — and it still has a bug
   the whole school sits a paper out of 200 (1200 students): 15 report cards get the wrong grade
   wrong scores: [34.5, 44.5, 74.5] · 5 students told they FAILED at 34.5
   found by one test at Dipika's desk: 1 failing check, 0 students hurt · found after the cards are posted:
   15 reprints, 5 apology letters, a re-marking meeting — the later a bug is found, the more it costs
   a passing test proves the code works for THAT input; it cannot prove there is no bug (5/5 passed above)

═══ unit ═══
── the tiny runner: 4 exam questions for grade_v1 (arrange · act · assert)
   distinction            pass
   fail_below_pass_mark   pass
   just_a_distinction     FAIL — expected 'A', got 'B'
   out_of_range           ERROR — ValueError: score out of range: 101
   2 passed, 2 failed · a FAIL is a wrong answer, an ERROR is a crash the test did not expect
── stdlib unittest runs exam/suites/test_grades_unittest.py against grade(): 6 tests · 0 failures · 0 errors
   same shape, more help: setUp/tearDown, assertEqual/assertRaises, discovery, -v; pytest is the popular third-party runner

═══ design ═══
── equivalence classes: one question per class is enough to start
     -5 → refused     20 → F     40 → D     50 → C     65 → B     90 → A    150 → refused
── boundary values: v1 vs the fixed grade()
    34.4: v1 F · fixed F
    34.5: v1 F · fixed D   ← BUG
      35: v1 D · fixed D
    44.5: v1 D · fixed C   ← BUG
      45: v1 C · fixed C
    59.5: v1 B · fixed B
      60: v1 B · fixed B
    74.4: v1 B · fixed B
    74.5: v1 B · fixed A   ← BUG
      75: v1 A · fixed A
   every half-mark 0–100 (201 scores): v1 is wrong at [34.5, 44.5, 74.5] — round() sends halves to the EVEN number
── decision table: promoted = average passes AND (attendance ≥ 75% OR a medical note)
   average 70 · attendance 90% · note no  → promoted
   average 70 · attendance 90% · note yes → promoted
   average 70 · attendance 60% · note no  → held back
   average 70 · attendance 60% · note yes → promoted
   average 30 · attendance 90% · note no  → held back
   average 30 · attendance 90% · note yes → held back
   average 30 · attendance 60% · note no  → held back
   average 30 · attendance 60% · note yes → held back
   3 conditions → 8 rules → 8 tests; a table shows the rule nobody wrote down

═══ fixtures ═══
── a_student() — a prepared answer sheet with sensible defaults → Katrina · average 75.0 · A · promoted True
   .with_attendance(60) → promoted False · + .with_medical_note() → promoted True
   .named('Dipika').with_marks(maths=20, science=30, english=40) → average 30.0 · F
── one SHARED answer sheet: order enrol→empty: 1 passed, 1 failed · order empty→enrol: 2 passed, 0 failed
   a FRESH sheet per test (setup): 2 passed, 0 failed · reversed: 2 passed, 0 failed · teardown ran 2 times
   independent tests pass in any order, alone or together — the order must never be part of the answer

═══ doubles ═══
── the attendance service is far away; stand-in examiners take its place
   stub  (canned answer)      → attendance 86.0% · eligible True
   fake  (a small working one) → student 42: 86.0% · student 7: 70.0% · eligible False
   spy   (records calls)      → calls ['/attendance/42']
   mock  (unittest.mock)      → a 503 raises AttendanceUnavailable; assert_called_once_with('/attendance/42') holds
── over-mocking: the client now asks '/attendance/42?term=1' — same answers, new URL
   fake-based test: eligible True → passes · mock-based test → FAILS (the mock checked HOW, not WHAT)
   and a stub can lie: it returned {'days_present': ...} but what if the real service says 'present'? → KeyError 'days_present' in production
   double what you do not own or cannot run fast; check the doubles against the real thing (lesson 07)

═══ integration ═══
── Katrina 68 and Dipika 67 in maths; the class average should be 67.5
   unit test with a Python fake store → 67.5 ✅ (Python's / keeps the half)
   integration test, REAL SQLite in memory, v1 SQL SUM/COUNT → 67 ❌ (INTEGER / INTEGER drops it)
   fixed SQL AVG(marks) → 67.5 ✅
   save Katrina's maths twice: fake silently overwrites · SQLite → IntegrityError (the PRIMARY KEY the fake never had)
── exam/suites/test_store_sqlite.py: 3 tests · 0 failing · a fresh :memory: database per test
   integration tests catch the bugs that live BETWEEN your code and the real thing: SQL, types, constraints

═══ contract ═══
── the consumer (report cards) writes down what it needs: GET /attendance/42 → 200 with days_present:int, days_total:int
   provider v1 as today                  → ✅ honours the contract
   provider v2 adds 'late_days'          → ✅ honours the contract
   provider v3 renames to 'present'      → ❌ missing field 'days_present'
   provider v4 sends days_total as text  → ❌ 'days_total' is str, expected int
   the provider runs the consumer's contract in ITS pipeline — v3 and v4 are stopped before they ship
   a new field is fine (the consumer ignores what it did not ask for); renames and type changes are not

═══ pyramid ═══
── one end-to-end run through the teaching app: database → attendance → report card → Aishwarya: average 74.33 · B · attendance 86.0% · promoted True
── a cost MODEL (illustrative numbers): unit 0.005 s, integration 0.1 s, e2e 5 s per test; flake chance 0.01% / 0.05% / 1%
   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
   both good shapes keep e2e tests FEW; they disagree on whether most tests are unit or integration

═══ property ═══
── property: for every whole n from 0 to 99, grade(n + 0.5) == grade(n + 1) — halves round UP
   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
   grade    property 'a higher score never gets a lower grade', 300 runs → failures: None
   grade_v1 passes the second property — round() never goes DOWN as the score goes up; a property finds only what it states
   the seed is printed so a failure can be replayed; shrinking hands you a simpler failing case than the first one found

═══ mutation ═══
── WEAK suite (7 questions): line coverage of grade() 12/12 = 100% · mutants killed 4/10
   s >= 75               → s > 75         SURVIVED
   s >= 35               → s > 35         SURVIVED
   s >= 60               → s >= 61        SURVIVED
   round_half_up(score)  → round(score)   SURVIVED
   return "A"            → return "B"     killed
   return "F"            → return "D"     killed
   score < 0 or          → score < 0 and  killed
   s >= 45               → s <= 45        killed
   score > 100           → score > 101    SURVIVED
   s >= 75               → s > 74         SURVIVED
── STRONG suite (21 questions): line coverage of grade() 12/12 = 100% · mutants killed 9/10
   the survivor of the STRONG suite: 's >= 75' → 's > 74' — s is a whole number, so this mutant is EQUIVALENT (no test can kill it)
   coverage says which lines RAN; mutation testing asks whether any test would NOTICE if they were wrong

═══ flaky ═══
── the wobbly clock on the wall: run each test 1000 times with NO code change
   time: due today, handed in within the hour    passed  967 · failed  33 → reads the real clock (fails after 23:00)
   randomness: 3 unseeded scores, one passes     passed  964 · failed  36 → unseeded random data
   waiting: sleep 200 ms, then check the job     passed  924 · failed  76 → a fixed sleep instead of waiting for done
   order: shared ROLL list, all 6 orders of 3 tests → 5 orders fail · fresh ROLL per test → 0 fail
── fixed: an injected clock → passed 1000, failed 0 · a seed → the same 3 scores every run · wait for 'done', not for time
   a test that both passes and fails on the same code is flaky — quarantine it, find the variable, pin it down

═══ ci ═══
── TDD for a new rule: a student 2 marks short of 35 WITH a medical note is lifted to 35
   RED      (test first, no code)  → 0/5 pass
   GREEN    (simplest code)        → 5/5 pass
   REFACTOR (name the rule)        → 5/5 pass
── test selection: a change to the attendance client
   runs 3 of 6 suites, 28 of 105 tests: unit/test_attendance, contract/test_attendance_pact, e2e/test_report_flow
   a change to grade() runs 69 of 105 tests · merging to main still runs all 105
   CI order: lint → unit → integration → contract → e2e — fast checks first, stop at the first red
── the whole picture: questions (unit) → the right questions (design) → fresh sheets (fixtures) → stand-ins (doubles) → real database (integration) → promises (contracts) → the pyramid → properties → mutants → a steady clock → CI

✅ done — every paper marked
🎒 धडा 01 आधी: तुम्हाला फक्त Python 3 हवे — दुसरे काहीही नाही. lab हा deterministic शिकवणी models चा संच आहे: property tester काही ओळींचा आहे, Hypothesis नाही; coverage counter हा sys.settrace hook आहे, coverage.py नाही; mutation tester मध्ये हाताने लिहिलेले 10 mutants आहेत, mutmut ने तयार केलेले नाहीत. खरी tools — pytest, Hypothesis, Pact, Playwright, coverage.py, mutmut — प्रत्येक धड्यात येतात. हे कुठे बसते: CI/CD शाळा हे tests pipelines मध्ये चालवते; API शाळा ते तपासत असलेले contracts design करते.

🗺️ संपूर्ण चित्र — एक आकृती, पूर्ण कोर्स

संपूर्ण कोर्स एका कॅनव्हासवर. 4K आवृत्तीसाठी क्लिक करा.

The big picture: the basics (why test, unit tests, test design, fixtures), beyond one unit (test doubles, integration, contract tests, end-to-end and the pyramid) and how good are the tests (property-based testing, coverage vs mutation, flaky tests, TDD and CI)

📝 भाग 1 — मूलभूत गोष्टी (धडे 1–4)

एक git branch = एक कल्पना; branch 04 मध्ये धडे 01–04 आहेत. exam/ ची प्रत्येक run सारखीच असते — randomness ला seed दिलेला आहे.

1

💸 test का करायचे

बाकावरच सापडलेला bug एका ओळीत सुधारतो; निकालपत्रे पाठवल्यानंतर सापडला तर 15 पुनर्छपाई लागतात. test काय सिद्ध करतो — आणि काय करू शकत नाही.lesson-01-why-testधडा वाचा →आकृती पहा ↗
2

✏️ Unit tests

Arrange, act, assert — परीक्षेचा एक प्रश्न, एका छोट्या runner मध्ये आणि standard library च्या unittest मध्ये.lesson-02-unit-testsधडा वाचा →आकृती पहा ↗
3

🎯 Test design

Equivalence classes, boundary values आणि decision tables — आणि 34.5 वरचा rounding bug.lesson-03-test-designधडा वाचा →आकृती पहा ↗
4

🗂️ Fixtures आणि test data

तयार उत्तरपत्रिका: builders, setup आणि teardown, आणि कोणत्याही क्रमाने pass होणारे tests.lesson-04-fixturesधडा वाचा →आकृती पहा ↗

🧑‍🏫 भाग 2 — एका unit च्या पलीकडे (धडे 5–8)

जेव्हा code इतर गोष्टींशी बोलतो: doubles, खरा database, teams मधील contracts, आणि प्रत्येक प्रकारचे किती tests.

5

🧑‍🏫 Test doubles

बदली परीक्षक: stub, fake, spy आणि mock — आणि जास्त mocking मुळे चुकीच्या गोष्टीची test कशी होते.lesson-05-test-doublesधडा वाचा →आकृती पहा ↗
6

🔌 Integration tests

memory मधील खरा SQLite database fake ला न सापडलेला bug शोधतो: 67, 67.5 नाही.lesson-06-integrationधडा वाचा →आकृती पहा ↗
7

🤝 Contract tests

consumer त्याला काय हवे ते लिहून ठेवतो; provider प्रत्येक release त्याच्याशी तपासतो.lesson-07-contract-testsधडा वाचा →आकृती पहा ↗
8

🔺 E2E आणि pyramid

End-to-end tests, आणि वेळ व flakiness मध्ये pyramid विरुद्ध trophy विरुद्ध ice-cream cone.lesson-08-e2e-pyramidधडा वाचा →आकृती पहा ↗

🔬 भाग 3 — tests किती चांगले आहेत (धडे 9–12)

tests चीच परीक्षा: generate केलेले inputs, mutants, flakiness — आणि योग्य वेळी योग्य tests चालवणे.

9

🎲 Property-based testing

एक नियम सांगा, एका seed पासून 100 inputs generate करा, आणि failure ला सोप्या case पर्यंत shrink करा.lesson-09-property-basedधडा वाचा →आकृती पहा ↗
10

🧬 Coverage विरुद्ध mutation

100% line coverage जी 10 पैकी फक्त 4 mutants मारते — coverage काय चालले ते दाखवते, काय तपासले ते नाही.lesson-10-coverage-mutationधडा वाचा →आकृती पहा ↗
11

🕰️ Flaky tests

डुगडुगणारे घड्याळ: वेळ, क्रम, randomness आणि वाट पाहणे — पुन्हा चालवून सापडतात, त्यांना पक्के करून सुधारतात.lesson-11-flaky-testsधडा वाचा →आकृती पहा ↗
12

🚦 CI, TDD आणि संपूर्ण चित्र

Red, green, refactor; बदलाला लागू असलेलेच tests चालवा — आणि संपूर्ण चित्र.lesson-12-ci-tddधडा वाचा →आकृती पहा ↗
🗣️ हे मोठ्याने समजावून सांगा — धडा 12 नंतर: (1) 5 pass होणाऱ्या tests नी grade_v1 बरोबर असल्याचे का सिद्ध झाले नाही? (2) FAIL आणि ERROR मध्ये काय फरक आहे? (3) equivalence classes ना न सापडणारे bugs boundary values ना का सापडतात? (4) प्रत्येक test ला नवीन fixture का मिळायला हवे? (5) stub खोटे कधी बोलतो, आणि ते काय पकडते? (6) fake ने 67.5 दिले तेव्हा खऱ्या database ने 67 का दिले? (7) contract साठी field जोडणे सुरक्षित पण एखाद्याचे नाव बदलणे का नाही? (8) end-to-end tests कमीच का ठेवायचे? (9) shrinking काय करते? (10) 100% coverage 10 पैकी फक्त 4 mutants कसे मारू शकते? (11) flaky tests ची चार कारणे सांगा. (12) code च्या आधी test का लिहायचा?
🎓 याच शाळेतून: CI/CD · API · Database · Distributed Systems — तेच उपमांचे विश्व, तीच branch-दर-branch पद्धत.

📐 धड्यांच्या आकृत्या — क्रमांक अनुसरा

प्रत्येक धडा एका क्रमांकित बॉक्स-आणि-बाण आकृतीत — स्वतंत्र पानावरही.

1 💸 test का करायचे

बाकावरच सापडलेला bug एका ओळीत सुधारतो; निकालपत्रे पाठवल्यानंतर सापडला तर 15 पुनर्छपाई लागतात. test काय सिद्ध करतो — आणि काय करू शकत नाही.

🧒 सोप्या शब्दांत

दीपिकाने गुणांचे grade बनवणारा code लिहिला. कतरिनाने त्याला 5 सोपे प्रश्न विचारले, आणि पाचही बरोबर आले. मग 1200 विद्यार्थ्यांनी 200 गुणांचा पेपर दिला. 34.5% असलेली मुलगी पास व्हायला हवी होती, पण code ने F सांगितले. 15 report cards चुकीचे आले. दीपिकाच्या डेस्कवर अजून एक प्रश्न विचारला असता तर हे एका मिनिटात पकडले गेले असते.

📖 नवे शब्दtest — code साठी exam question, ज्याचे बरोबर उत्तर आधीच माहीत असतेbug — code मधली चूक, जसे 34.5 ला D ऐवजी F मिळणेround half to even — Python चे round(): 34.5 वर 35 न जाता सम 34 वर जाते
1📝 कतरिना 5 सोपे प्रश्न विचारतेप्रश्नपत्रिका · grade_v1grade_v1(90)→ Aउत्तर: Agrade_v1(65)→ Bउत्तर: Bgrade_v1(50)→ Cउत्तर: Cgrade_v1(40)→ Dउत्तर: Dgrade_v1(10)→ Fउत्तर: FDipikagrade_v1 लिहिले5 / 5 ✓कोणताही प्रश्न.5 ने संपत नाही2🔍 round() अर्धा भाग सम (EVEN) शेजाऱ्याकडे पाठवतेशाळेचा नियम: अर्धे वर जातात · Python round(): अर्धे सम संख्येकडे जातात343534.5round → 34 → Fनियम → 35 → D444544.5round → 44 → Dनियम → 45 → C596059.5round → 60 → Bनियम → 60 → Bनशीब747574.5round → 74 → Bनियम → 75 → Aलाल: grade_v1 काय करते · हिरवी तुटक रेषा: शाळेचा नियम3💸 तोच bug, दोन वेगवेगळ्या वेळी सापडलेला① दीपिकाच्या बाकावर, छपाईपूर्वीआणखी एक प्रश्नexpect(grade_v1(34.5), "D")FAIL: expected D, got F1 अयशस्वी तपासणी0 विद्यार्थ्यांना त्राससुधारणा: 1 ओळखर्च: काही मिनिटेround(score) ऐवजी round_half_up(score)② 1200 निकालपत्रे पाठवल्यानंतर (200 गुणांची प्रश्नपत्रिका)34.5% → F, D हवा होता5 विद्यार्थी#1F34.5%#2F34.5%#3F34.5%#4F34.5%#5F34.5%44.5% → D, C हवा होता6 विद्यार्थी#1D44.5%#2D44.5%#3D44.5%#4D44.5%#5D44.5%#6D44.5%74.5% → B, A हवा होता4 विद्यार्थी#1B74.5%#2B74.5%#3B74.5%#4B74.5%= 15 चुकीची निकालपत्रे · 34.5 वर 5 विद्यार्थ्यांना नापास (FAILED) सांगितले15 पुनर्छपाई5 माफीपत्रेपुनर्तपासणीची बैठकbug जितका उशिरा सापडतो, तितका तो महाग पडतो — आणि pass होणारा test फक्त त्याच input बद्दल सिद्ध करतो
⏪ आधी

दीपिकाने grade_v1 लिहिले आणि नजरेने तपासले; ते बरोबर दिसले, म्हणून ते थेट report cards छापायला गेले.

💡 काय

Test म्हणजे code साठी exam question, ज्याचे बरोबर उत्तर आधीच माहीत असते. grade_v1 पाच सोपे प्रश्न सोडवते: 5 पैकी 5.

⚙️ कसे

1200 विद्यार्थी, 200 गुणांचा पेपर: round(34.5) = 34, म्हणून 34.5, 44.5 आणि 74.5 वर 15 report cards चुकीचे येतात.

🎯 का

दीपिकाच्या डेस्कवर एक प्रश्न म्हणजे एक failing check; cards पाठवल्यानंतर 15 reprints आणि 5 माफीपत्रे.

🚀 पुढे

पुढच्या धड्यात खरा exam question: arrange, act, assert, आणि FAIL व ERROR तपासणारा छोटा runner.

🧪 इथे करून पाहा — संपूर्ण शाळा एक पेपर देते — पेपर, वर्ग आणि rounding बदला, आणि चुकीची निकालपत्रे मोजा
12002026

पूर्ण धडा 01 वाचा →

2 ✏️ Unit tests

Arrange, act, assert — परीक्षेचा एक प्रश्न, एका छोट्या runner मध्ये आणि standard library च्या unittest मध्ये.

🧒 सोप्या शब्दांत

चांगल्या exam question च्या तीन पायऱ्या असतात. आधी तयारी: गुण 82 आहेत. मग एकदा विचारा: grade काय? मग answer key शी जुळवा: ते A हवे. कतरिनाने असे 4 प्रश्न लिहिले. दोन pass झाले. एकाचे उत्तर चुकले, तो FAIL. एकाने code crash केला, तो ERROR.

📖 नवे शब्दarrange · act · assert — तयारी करा, एकदा विचारा, मग answer key शी जुळवाFAIL — code ने चुकीचे उत्तर दिले, जसे A हवे असताना BERROR — test ला अपेक्षित नसताना code crash झालाtest runner — प्रत्येक test चालवून pass, FAIL किंवा ERROR लिहिणारा मदतनीस
1✏️ परीक्षेचा एक प्रश्न = arrange · act · assert🧾 ARRANGEपार्श्वभूमी तयार कराscore = 82✋ ACTप्रश्न एकदाच विचाराresult = grade_v1(score)✅ ASSERTउत्तरसूचीशी तुलना कराexpect(result, "A")82scoregrade_v1कोड82Aउत्तरसूचीA==Apass2🏃 छोटा runner कतरिनाचे 4 प्रश्न तपासतोगुणपत्रक · grade_v1distinction()passfail_below_pass_mark()passjust_a_distinction()FAILexpected 'A', got 'B'out_of_range()ERRORValueError: score out of range: 101testनिकाल2 passed, 2 failed3❌ FAIL विरुद्ध 💥 ERRORBFAIL = चुकीचे उत्तरgrade_v1(74.5) → Bउत्तरसूची म्हणते Aविद्यार्थिनीने चुकीचे उत्तर दिले!ERROR = crashgrade_v1(101) फेकतोValueError, जी test लाअपेक्षित नव्हती — तपासलीही गेली नाहीship करण्यापूर्वी दोन्ही तपासायला हवे4🧰 standard library मध्ये हाच आकार: दुरुस्त केलेल्या grade() वर unittestexam/suites/test_grades_unittest.py: 6 tests · 0 failures · 0 errorssetUp / tearDown · assertEqual / assertRaises · discovery · -v · pytest हा लोकप्रिय runner आहे
⏪ आधी

तपासणी म्हणजे print() ओळी, ज्या कोणीतरी डोळ्यांनी वाचायच्या; छापलेला grade गुपचूप बदलला तरी कोणाला कळत नसे.

💡 काय

Unit test score ठरवते (arrange), grade_v1(score) एकदा चालवते (act), आणि answer key शी जुळवते (assert).

⚙️ कसे

कतरिनाचे 4 प्रश्न: 2 pass, 74.5 FAIL (अपेक्षित 'A', मिळाले 'B') आणि 101 ERROR: ValueError out of range.

🎯 का

FAIL म्हणजे चुकीचे उत्तर; ERROR म्हणजे test ला अपेक्षित नसलेला crash. unittest 6 tests चालवते: 0 failures, 0 errors.

🚀 पुढे

पुढचा धडा विचारतो कोणते प्रश्न लिहायचे: equivalence classes, boundary values आणि decision table.

🧪 इथे करून पाहा — परीक्षेचा एक प्रश्न लिहा — score ठरवा (arrange), एका version ने चालवा (act), उत्तर तपासा (assert)

पूर्ण धडा 02 वाचा →

3 🎯 Test design

Equivalence classes, boundary values आणि decision tables — आणि 34.5 वरचा rounding bug.

🧒 सोप्या शब्दांत

कतरिना प्रत्येक गुणाबद्दल विचारू शकत नाही. म्हणून ती गट करते: 45 ते 59 मधल्या सगळ्या गुणांना C, म्हणून एक प्रश्न पुरेसा. सात प्रश्न, सगळे pass. मग ती कडांवर विचारते: 34.4, 34.5 आणि 35. 34.5 वर code F म्हणतो पण नियम D म्हणतो. पकडले! चुका कडांवरच लपतात.

📖 नवे शब्दequivalence class — सारखे वागायला हवेत असे inputs चा गट, जसे C चे सगळे गुणboundary value — दोन गटांच्या कडेवरची किंमत, जसे 35 जवळचे 34.5decision table — प्रत्येक हो/नाही जोडीचा तक्ता: 3 अटींचे 8 नियम
1🗂️ equivalence classes — एका पट्ट्यातील प्रत्येक score ला एकच grade मिळतो, म्हणून प्रत्येक पट्ट्यासाठी एकच प्रश्न विचारानाकारलेनाकारलेFDCBA035456075100< 0> 100-5 → नाकारले20 → F40 → D50 → C65 → B90 → A150 → नाकारले7 प्रश्न, प्रत्येक class साठी एक — grade_v1 वरही सातही pass होतात. तरीही bug तसाच आहे.score आधी round होतो म्हणून पट्टे खरे तर x.5 वर विभागतात: 34.5 आधीच D मध्ये येतो2🚧 boundary values — अगदी कडांवर विचारा: v1 विरुद्ध दुरुस्त केलेला grade()scoregrade_v1grade34.4FF34.5FD↑ BUG35DD44.5DC↑ BUG45CC59.5BB60BB74.4BB74.5BA↑ BUG75AA0–100 मधला प्रत्येक अर्धा गुण (201 scores): v1 [34.5, 44.5, 74.5] वर चुकतो59.5 फक्त नशिबाने बरोबर येतो: round(59.5) सम 60 कडे जातो — वर round करण्यासारखेचbugs classes मधील कडांवर राहतात: प्रत्येक रेषेच्या अगदी खालचे, रेषेवरचे आणि अगदी वरचे मूल्य विचारा3📋 decision table — पुढच्या वर्गात = सरासरी pass AND (हजेरी ≥ 75% OR वैद्यकीय चिठ्ठी)सरासरी pass?हजेरी ≥ 75%?वैद्यकीय चिठ्ठी?पुढच्या वर्गात?नियम 1हो (70)हो (90%)नाहीपुढच्या वर्गातनियम 2हो (70)हो (90%)होपुढच्या वर्गातनियम 3हो (70)नाही (60%)नाहीत्याच वर्गातनियम 4हो (70)नाही (60%)होपुढच्या वर्गातनियम 5नाही (30)हो (90%)नाहीत्याच वर्गातनियम 6नाही (30)हो (90%)होत्याच वर्गातनियम 7नाही (30)नाही (60%)नाहीत्याच वर्गातनियम 8नाही (30)नाही (60%)होत्याच वर्गातकोणीही न लिहिलेला नियम3 अटी → 8 नियम → 8 tests
⏪ आधी

कतरिनाने मनात आलेले गुण निवडले: 90, 65, 50, 40, 10. सगळे pass झाले, आणि rounding bug लपूनच राहिला.

💡 काय

Equivalence classes प्रत्येक band मधून एक गुण घेतात; boundary values प्रत्येक कडेच्या थोडे खाली, नेमके आणि वर विचारतात.

⚙️ कसे

34.5 वर grade_v1 म्हणते F, दुरुस्त grade म्हणते D. सर्व 201 half-marks पैकी v1 फक्त 34.5, 44.5 आणि 74.5 वर चुकते.

🎯 का

Bugs कडांवर राहतात. Decision table 3 हो/नाही अटींचे 8 नियम करते, म्हणून promotion चा कोणताही प्रकार सुटत नाही.

🚀 पुढे

पुढच्या धड्यात answer sheets तयार होतात: योग्य defaults असलेले builders, आणि प्रत्येक test साठी नवा fixture.

🧪 इथे करून पाहा — score कडांपर्यंत सरकवा; मग decision table च्या तीन अटी ठरवा
34.57090

पूर्ण धडा 03 वाचा →

4 🗂️ Fixtures आणि test data

तयार उत्तरपत्रिका: builders, setup आणि teardown, आणि कोणत्याही क्रमाने pass होणारे tests.

🧒 सोप्या शब्दांत

परीक्षेपूर्वी office प्रत्येक विद्यार्थिनीसाठी answer sheet तयार करते. कतरिनाकडेही एक तयार विद्यार्थिनी आहे: चांगले गुण आणि 90% attendance. कमी attendance तपासायला ती फक्त तेच 60% करते. एकदा दोन प्रश्नांनी एकच sheet वापरली. पहिल्याने त्यावर ऐश्वर्याचे नाव लिहिले, आणि दुसऱ्याला ती कोरी हवी होती. म्हणून आता प्रत्येक प्रश्नाला नवी sheet मिळते.

📖 नवे शब्दfixture — test सुरू होण्यापूर्वी लागणारी तयार सामग्री, जसे कोरी answer sheetbuilder — सामान्य विद्यार्थिनी बनवणारा मदतनीस, म्हणजे test फक्त एकच गोष्ट बदलतेsetup and teardown — प्रत्येक test आधी नवी sheet द्या आणि नंतर परत घ्या
1🏗️ a_student() — तयार उत्तरपत्रिका; प्रत्येक test फक्त तिच्या विषयापुरतेच बदलतेdefaultsगणित 80विज्ञान 70इंग्रजी 75हजेरी 90%चिठ्ठी नाहीa_student()प्रगतिपुस्तकnameKatrinaसरासरी75.0gradeAहजेरी90%पुढच्या वर्गातTrue.with_attendance(60)प्रगतिपुस्तकnameKatrinaसरासरी75.0gradeAहजेरी60%पुढच्या वर्गातFalse+ .with_medical_note()प्रगतिपुस्तकnameKatrinaसरासरी75.0gradeAहजेरी60%पुढच्या वर्गातTrue.named('Dipika')…प्रगतिपुस्तकnameDipikaसरासरी30.0gradeFहजेरी90%पुढच्या वर्गातFalse.with_marks(maths=20,science=30, english=40)पिवळे = प्रत्येक test ने बदललेली एकच गोष्ट · बाकी सर्व सामान्य, pass होणारी विद्यार्थिनीच राहतेम्हणून fail होणारी test थेट तिच्या विषयाकडे बोट दाखवते — आणि प्रगतिपुस्तकातील नवीन field एकच builder मोडते, 50 tests नाही2😬 दोन tests एकच उत्तरपत्रिका वापरतातक्रम enrol → emptyहजेरीपटAishwaryaenrol()'Aishwarya' लिहितेpassतीच पत्रिकाहजेरीपटAishwaryaempty()कोरी पत्रिका अपेक्षितFAIL — कोरी नाही1 passed, 1 failedक्रम empty → enrolहजेरीपटempty()कोरी पत्रिका अपेक्षितpassतीच पत्रिकाहजेरीपटAishwaryaenrol()'Aishwarya' लिहितेpass2 passed, 0 failedनिकाल कोण आधी गेले यावर अवलंबून3✅ प्रत्येक test ला नवी पत्रिका: setup → test → teardownक्रम enrol → emptyउभारणीऐश्व.enrol()teardownउभारणीempty()teardown2 passed, 0 failedक्रम empty → enrolउभारणीempty()teardownउभारणीऐश्व.enrol()teardown2 passed, 0 failedteardown 2 वेळा चालला — fail झालेल्या test ची पत्रिकाही गोळा केली जातेस्वतंत्र tests कोणत्याही क्रमाने, एकट्या किंवा एकत्र pass होतात
⏪ आधी

प्रत्येक test स्वतःची विद्यार्थिनी हाताने बनवत असे, आणि दोन tests एकच roll list वापरत, ज्यावर पहिलीने आधीच लिहिलेले असे.

💡 काय

Fixture म्हणजे तयार answer sheet. a_student() म्हणजे कतरिना: average 75.0, grade A, promoted True, 90% attendance.

⚙️ कसे

.with_attendance(60) ने promoted False होते; .with_medical_note() जोडल्यावर True. प्रत्येक test एकच गोष्ट बदलते.

🎯 का

एकच shared sheet: enrol → empty केल्यास 1 passed, 1 failed. प्रत्येक test ला नवी sheet: दोन्ही क्रमात 2 passed.

🚀 पुढे

पुढच्या धड्यात दूरच्या attendance service ऐवजी पर्यायी परीक्षक येतात: stub, fake, spy आणि mock.

🧪 इथे करून पाहा — a_student() मधून विद्यार्थिनी तयार करा, मग दोन tests सामायिक किंवा नव्या उत्तरपत्रिकेवर चालवा
80707590

पूर्ण धडा 04 वाचा →

5 🧑‍🏫 Test doubles

बदली परीक्षक: stub, fake, spy आणि mock — आणि जास्त mocking मुळे चुकीच्या गोष्टीची test कशी होते.

🧒 सोप्या शब्दांत

खऱ्या attendance परीक्षिका दुसऱ्या इमारतीत बसतात आणि कधी कधी रजेवर असतात. म्हणून कतरिना पर्यायी परीक्षक वापरते. Stub कडे एकच card असते: 200 पैकी 172 दिवस. Fake कडे विद्यार्थिनींची छोटी चालणारी वही असते. Spy तिला विचारलेला प्रत्येक प्रश्न लिहून ठेवते. Mock ला एकच ठरलेला प्रश्न अपेक्षित असतो आणि दुसरा आला की ती तक्रार करते.

📖 नवे शब्दtest double — हळू किंवा दूरच्या गोष्टीच्या जागी वापरलेला पर्यायstub — नेहमी तेच ठरलेले उत्तर देणारा पर्यायfake — छोटा पण चालणारा पर्याय, जसे खरोखर विद्यार्थिनी शोधू शकणारी वहीmock — कोणता नेमका call येणार हे आधीच सांगितलेला पर्याय
1🧑‍🏫 हजेरीची service दूर आहे — चार बदली परीक्षक तिची जागा घेतातAttendanceClienttest होणारा codeeligible(42)?≥ 75% असल्यास पात्रtransport(path) ला विचारतोSTUBstatus 200172 / 200 दिवसstub — आधीच ठरवलेले उत्तर→ हजेरी 86.0% · eligible True"या उत्तराचे code काय करतो?"FAKE42: 172 / 200 7: 140 / 200fake — छोटा, चालणारा→ 42: 86.0% · 7: 70.0% · eligible(7) Falseखरोखर विद्यार्थिनी शोधणारा dictSPYcalls:'/attendance/42'spy — प्रत्येक call लिहून ठेवतो→ calls ['/attendance/42']प्रत्येक call fake कडे पुढे पाठवतोMOCK503 परत करतोएकदा अपेक्षित:'/attendance/42'mock — काय अपेक्षित आहे ते सांगितलेला→ 503 आल्यास AttendanceUnavailable फेकला जातोassert_called_once_with('/attendance/42') खरे ठरतेखरीहजेरीसेवादूर · हळूकधी कधी बंदproduction मध्ये जोडतातखरा HTTP call;test मध्ये जोडतातबदलीजे तुमचे नाही किंवा पटकन चालवता येत नाही त्याचा double वापरा — test होणाऱ्या code ला कधीच कळत नाही की त्याला कोणता मिळाला2🔁 दीपिका URL बदलते, उत्तर नाही'/attendance/42' → '/attendance/42?term=1'fake-आधारित testmock-आधारित test42: 172 / 200 7: 140 / 200विद्यार्थिनी शोधतो →eligible Truepass — काय (WHAT) तपासलेएकदा अपेक्षित:'/attendance/42'मिळाले '…/42?term=1'तेच उत्तर, नवे शब्दFAIL — कसे (HOW) तपासलेतुम्हाला महत्त्वाच्या निकालांवर assert करा, प्रत्येक call वर नाही3🤥 आणि stub खोटं बोलू शकतोstub आठवणीतून लिहिला होता:{"days_present": 172, "days_total": 200}खरी service म्हणते:{"present": 172, "total": 200}test पास होतेKeyError 'days_present'production मध्येdoubles खऱ्या गोष्टीशी तपासून पाहा→ धडा 07 या तपासणीला contract बनवतो
⏪ आधी

प्रत्येक test खऱ्या attendance service ला बोलवत असे: हळू, कधी बंद, आणि ती रजेवर असली की test fail होत असे.

💡 काय

Test doubles म्हणजे पर्यायी परीक्षक: stub ठरलेले 172/200 देतो, fake विद्यार्थिनींची छोटी चालणारी dict ठेवतो.

⚙️ कसे

Stub: 86.0%, eligible True. Fake: pupil 7 ला 70.0%, eligible नाही. Spy '/attendance/42' नोंदवतो; 503 वर error.

🎯 का

URL मध्ये '?term=1' आले, उत्तरे तीच: fake test pass होते, mock test FAIL होते. त्याने 'काय' नव्हे, 'कसे' तपासले.

🚀 पुढे

पुढच्या धड्यात खोट्या marks book ऐवजी खरा SQLite database येतो, आणि fake मध्ये कधीच न येणारा bug सापडतो.

🧪 इथे करून पाहा — AttendanceClient मध्ये पर्यायी परीक्षक जोडा — आणि तो काय उत्तर देतो ते बदला
172

पूर्ण धडा 05 वाचा →

6 🔌 Integration tests

memory मधील खरा SQLite database fake ला न सापडलेला bug शोधतो: 67, 67.5 नाही.

🧒 सोप्या शब्दांत

कतरिनाला maths मध्ये 68 आणि दीपिकाला 67 मिळाले, म्हणून वर्गाची सरासरी 67.5. Python मधल्या खोट्या marks book ने 67.5 सांगितले. खऱ्या database ने 67 सांगितले! Database मध्ये पूर्ण संख्येला पूर्ण संख्येने भागले की अर्धा भाग गळतो. खोट्या वहीत हा bug कधीच येऊ शकला नसता. फक्त खऱ्या database सोबतच्या test ने तो शोधला.

📖 नवे शब्दintegration test — तुमचा code खऱ्या गोष्टीसोबत चालवणारी test, जसे databaseinteger division — पूर्ण संख्येला पूर्ण संख्येने भागणे, ज्यात अर्धा भाग जातो: 135 / 2 = 67constraint — database पाळायला लावतो असा नियम, जसे प्रत्येक विषयात एका विद्यार्थिनीचा एकच mark
1🧸 unit test: एक काल्पनिक गुणपत्रक (Python dict)('Katrina','maths'): 68('Dipika','maths'): 67Python मधील बेरीज:(68 + 67) / 2= 135 / 2वर्गाची सरासरी = 67.5 ✅Python चा / अर्धा भाग ठेवतोकतरिनाचे गणिताचे गुण दोनदा save करा (68, मग 90):('Katrina','maths'): 68('Katrina','maths'): 90गुपचूप overwrite झालेdict ला PRIMARY KEY नसते2🗄️ integration test: memory मधील खरे SQLite:memory:विद्यार्थीsubjectगुणKatrinamaths68Dipikamaths67marks INTEGER · PRIMARY KEY (student, subject)SELECT SUM(marks) / COUNT(*)INTEGER / INTEGER → 67 ❌SQLite मध्ये 135 / 2 अर्धा भाग गाळतोSELECT AVG(marks)→ 67.5 ✅ (दुरुस्ती)कतरिनाला दोनदा save करा →IntegrityError: UNIQUE constraint failed: marks.student, marks.subject3📏 अर्धा भाग कुठे गेला — तेच दोन गुण, दोन उत्तरे66676869DipikaKatrina67.5 खरी सरासरीSQLite v1 अर्धा भाग गाळते → 67fake मध्ये हा bug असूच शकत नव्हता— फक्त खऱ्या database मध्येच असू शकतो4🧪 exam/suites/test_store_sqlite.py — प्रत्येक test साठी नवा :memory: databasesetUpclass_average अर्धा भाग ठेवते→ 67.5passsetUpv1 सरासरी अर्धा भाग गमावते→ 67passsetUpतीच विद्यार्थिनी + विषय दोनदा→ IntegrityErrorpass3 tests · 0 अयशस्वी · integration tests तुमचा code आणि खरी गोष्ट यांच्या मधे राहणारे bugs पकडतात: SQL, types, constraints
⏪ आधी

दीपिकाने class average फक्त Python dict वर तपासले, म्हणून production मध्ये चालणाऱ्या SQL ला कोणीच प्रश्न विचारला नाही.

💡 काय

Integration test तुमचा code खऱ्या गोष्टीसोबत चालवते: इथे memory मध्ये तयार केलेला खरा SQLite database.

⚙️ कसे

कतरिना 68, दीपिका 67: fake म्हणते 67.5, SQLite SUM/COUNT म्हणते 67 (INTEGER / INTEGER), आणि AVG(marks) म्हणते 67.5.

🎯 का

काही bugs code आणि database यांच्यामध्ये राहतात. कतरिनाचे maths दोनदा save: fake overwrite करते, SQLite IntegrityError देते.

🚀 पुढे

पुढचा धडा attendance service न चालवता तपासतो: consumer एक contract लिहितो, जो provider ने पाळायलाच हवा.

🧪 इथे करून पाहा — तेच गुण काल्पनिक dict मध्ये आणि खऱ्या SQLite table मध्ये — सरासरी वेगळ्या होताना पाहा
686774

पूर्ण धडा 06 वाचा →

7 🤝 Contract tests

consumer त्याला काय हवे ते लिहून ठेवतो; provider प्रत्येक release त्याच्याशी तपासतो.

🧒 सोप्या शब्दांत

कतरिनाच्या report cards ना attendance office कडून दोन आकडे लागतात: हजर दिवस आणि एकूण दिवस. ती ते एका card वर लिहून office च्या भिंतीवर लावते. Office आपला form बदलण्यापूर्वी तो तिच्या card शी जुळवून पाहते. नवा रकाना जोडणे चालते. days_present चे नाव बदलणे किंवा 200 अक्षरात लिहिणे नवा form बाहेर जाण्यापूर्वीच पकडले जाते.

📖 नवे शब्दcontract — एक बाजू काय पाठवते आणि दुसरीला काय लागते याचे लेखी वचनconsumer — data वापरणारी बाजू, इथे report cardsprovider — data पाठवणारी बाजू, इथे attendance office
1🤝 consumer त्याला काय हवे ते लिहून ठेवतो — provider प्रत्येक release त्याच्याशी तपासतोconsumerप्रगतिपुस्तके📌 contractconsumer: report-cardsprovider: attendanceGET /attendance/42→ 200 सोबतdays_present: intdays_total: intफक्त तो खरोखर वाचतो तेवढीच fields — बाकी काही नाहीwritesपुन्हा चालवले जातेprovider च्या CI मध्येproviderउपस्थिती कार्यालय2🏭 provider च्या चार releases, प्रत्येक ship होण्यापूर्वी कार्डाशी तपासली जातेv1 — आजच्यासारखे200 { "student": "42" "days_present": 172 "days_total": 200}✅ contract पाळतेship होतेv2 — 'late_days' जोडते200 { "student": "42" "days_present": 172 "days_total": 200 "late_days": 3}राखाडी: कधी मागितलेच नाही → दुर्लक्षित✅ contract पाळतेship होतेv3 — नाव बदलून 'present' करते200 { "student": "42" "present": 172 "days_total": 200}❌ 'days_present' field गायबship होण्यापूर्वीच थांबवलेv4 — days_total मजकूर म्हणून200 { "student": "42" "days_present": 172 "days_total": "200"}❌ 'days_total' str आहे, int अपेक्षितship होण्यापूर्वीच थांबवलेनवीन field चालते (consumer न मागितलेल्याकडे दुर्लक्ष करतो) · नाव बदलणे किंवा TYPE बदलणे चालत नाहीदोन्ही कार्यालये एकत्र चालवावी लागली नाहीत: प्रत्येक बाजू स्वतःला त्याच कार्डाशी तपासते (Pact हे प्रत्यक्षात करते)
⏪ आधी

Attendance office ने आपला form बदलला आणि नंतर सांगितले; पहिल्याच दिवशी report cards बिघडले.

💡 काय

Consumer-driven contract: report cards स्वतःला काय लागते ते लिहून ठेवतात, days_present: int आणि days_total: int.

⚙️ कसे

Provider आपल्या CI मध्ये contract पुन्हा चालवतो: v1 pass, v2 late_days जोडूनही pass, v3 आणि v4 थांबवले जातात.

🎯 का

नवीन field चालते; नाव बदलणे ('present') किंवा type बदलणे (days_total मजकूर म्हणून) चालत नाही, ship आधीच पकडले जाते.

🚀 पुढे

पुढच्या धड्यात शाळेचा पूर्ण दिवस end to end चालतो, आणि suite मध्ये कोणत्या प्रकारच्या किती tests हव्यात ते पाहतो.

🧪 इथे करून पाहा — provider चा response बदला आणि consumer च्या कार्डाशी तपासा

पूर्ण धडा 07 वाचा →

8 🔺 E2E आणि pyramid

End-to-end tests, आणि वेळ व flakiness मध्ये pyramid विरुद्ध trophy विरुद्ध ice-cream cone.

🧒 सोप्या शब्दांत

शाळा तपासायचे तीन मार्ग आहेत. डेस्कवरचा unit प्रश्न काही सेकंद घेतो. Marks book ची integration तपासणी थोडा जास्त वेळ घेते. शाळेचा पूर्ण दिवस सगळे एकत्र तपासतो, पण खूप वेळ लागतो आणि उशिरा आलेली bus तो बिघडवू शकते. म्हणून छोटे प्रश्न भरपूर आणि पूर्ण दिवस थोडेच ठेवा. बहुतेक पूर्ण दिवस म्हणजे वितळणारा ice-cream cone.

📖 नवे शब्दend-to-end test — database पासून report card पर्यंत पूर्ण app चालवणारी testtest pyramid — खूप unit tests, कमी integration tests, अगदी थोड्या e2e testsflaky failure — code शी काहीही संबंध नसलेल्या कारणाने fail होणारी test
1🏫 एक end-to-end run: शाळेचा पूर्ण दिवस, सगळे भाग एकत्रगुणांचा databaseगणित 81 · विज्ञान 68 · इंग्रजी 74हजेरी172 / 200 दिवसreport_card()श्रेणी · पुढच्या वर्गातप्रगतिपुस्तकnameAishwaryaसरासरी74.33gradeBहजेरी86.0%पुढच्या वर्गातTrueAishwaryaते सगळे एकत्र तपासते — आणि ते सर्वात हळू, सर्वात flaky प्रकारचे test आहे2⏱️ खर्चाचे MODEL: unit 0.005 s · integration 0.1 s · e2e 5 s प्रति test · flake 0.01% / 0.05% / 1%🔺 pyramid400 / 80 / 10 tests400 unit80 integration10 e2eप्रति run सेकंद60.0 s17%runs पैकी इतक्यांनाflaky failure आले🏆 trophy150 / 300 / 10 tests150 unit300 integration10 e2eप्रति run सेकंद80.8 s23%runs पैकी इतक्यांनाflaky failure आले🍦 ice-cream cone50 / 50 / 200 tests50 unit50 integration200 e2eप्रति run सेकंद1005.2 s87%runs पैकी इतक्यांनाflaky failure आलेदोन्ही चांगले आकार e2e tests कमीच ठेवतात; बहुतेक tests unit असावेत की integration यावर त्यांचे मत वेगळे आहे
⏪ आधी

Team बहुतेक पूर्ण app मधून click करून तपासत असे; प्रत्येक run ला खूप वेळ लागे आणि उशिरा आलेली bus ते वारंवार मोडत असे.

💡 काय

E2e test सगळे भाग एकत्र चालवते: database → attendance → report card, ऐश्वर्याला 74.33, B, promoted True.

⚙️ कसे

Cost model मध्ये pyramid ला एका run ला 60.0 s लागतात, trophy ला 80.8 s, आणि ice-cream cone ला 1005.2 s.

🎯 का

E2e tests जितक्या जास्त तितक्या flaky failures: pyramid चे 17% runs, trophy चे 23%, ice-cream cone चे 87%.

🚀 पुढे

पुढच्या धड्यात प्रश्न machine लिहिते: एक नियम सांगा, seed वरून 100 inputs तयार करा, आणि shrink करा.

🧪 इथे करून पाहा — suite ला आकार द्या — एक run किती वेळ घेतो, आणि किती वेळा flaky failure येते?
4008010

पूर्ण धडा 08 वाचा →

9 🎲 Property-based testing

एक नियम सांगा, एका seed पासून 100 inputs generate करा, आणि failure ला सोप्या case पर्यंत shrink करा.

🧒 सोप्या शब्दांत

कतरिना एकेक प्रश्नाऐवजी एक नियम लिहिते: .5 ने संपणाऱ्या गुणाला पुढच्या पूर्ण संख्येइतकाच grade मिळायला हवा. एक प्रश्न-यंत्र 100 आकडे निवडून नियम तपासते. 9 व्या प्रयत्नात ते 74 निवडते: 74.5 ला B, पण 75 ला A. मग ते सोपी चूक शोधते: 44, मग 34. उत्तर 34.5, तोच जुना bug.

📖 नवे शब्दproperty — फक्त एका उदाहरणासाठी नव्हे, प्रत्येक input साठी खरा असायला हवा असा नियमseed — फाशांचा सुरुवातीचा आकडा, म्हणजे तेच 100 प्रश्न पुन्हा चालवता येतातshrinking — fail होणारा input अजून fail होणाऱ्या सोप्या input मध्ये बदलणे: 74 → 44 → 34
1🎲 प्रश्नयंत्र: 0–99 मधील प्रत्येक पूर्ण संख्या n साठी, grade(n + 0.5) हे grade(n + 1) इतकेच असले पाहिजेforall(lambda n: grade_v1(n + 0.5) == grade_v1(n + 1), gen=rng.int(0, 99), runs=100, seed=…)seed 151#11730539417#6702249128#113827369853#167664767882#2115623134#2591#2651406078…run 25: 34.5 → F पण 35 → Dseed 354#12131256248#6124774#98650#11514850662#163233372621#2195465189#266678341…run 9: 74.5 → B पण 75 → Aहिरवा: नियम टिकला · लाल: पहिले failure · राखाडी: कधी विचारलेच नाही — पहिल्या failure वर यंत्र थांबते2🔍 seed 3 चे failure लहान करणे (shrinking): 74 → 44 → 347474.5 fail होतेलहान करून पाहा:0टिकतो37टिकतो64टिकतो54टिकतो44FAIL होते4444.5 fail होतेलहान करून पाहा:0टिकतो22टिकतो34FAIL होते3434.5 fail होतेलहान करून पाहा:0टिकतो17टिकतो24टिकतो14टिकतो4टिकतो33टिकतो→ यापेक्षा लहान काहीच fail होत नाही: थांबासर्वात लहान fail होणारे गुण 34.5 — धडा 03 मधलाच bug, आणि ते गुण कोणीही निवडले नाहीत3📜 त्याने आणखी काय तपासले?grade_v1अर्धे वर (UP) round होतातseed 1: run 25gradeअर्धे वर (UP) round होतात100 runs: Nonegrade_v1जास्त गुण, कधीच कमी श्रेणी नाही300 runs: Nonegradeजास्त गुण, कधीच कमी श्रेणी नाही300 runs: Nonegrade_v1 "जास्त गुण, कधीच कमी श्रेणी नाही" पास करते:गुण वाढताना round() कधीच खाली (DOWN)जात नाही — property फक्त तेच शोधतेजे ती सांगते.
⏪ आधी

कतरिना प्रत्येक test score हाताने निवडत असे, म्हणून code ला फक्त तिला सुचलेल्या आकड्यांबद्दलच विचारले जाई.

💡 काय

Property-based testing एक नियम सांगते, grade(n + 0.5) == grade(n + 1), आणि seeded machine 100 random n तपासते.

⚙️ कसे

Seed 3 वर grade_v1 run 9 मध्ये 74.5 ला fail होते; shrinking लहान n वापरून 74 → 44 → 34, म्हणजे 34.5.

🎯 का

हाताने एकही score न निवडता धडा 03 चा bug सापडला; दुरुस्त grade 100 runs pass करते, आणि seed failure पुन्हा दाखवतो.

🚀 पुढे

पुढचा धडा tests नाच गुण देतो: 100% coverage असूनही चुका सुटू शकतात, ज्या mutation testing पकडते.

🧪 इथे करून पाहा — प्रश्नयंत्र — seed, code आणि नियम बदला; ते fail होताना आणि लहान होताना पाहा
3100

पूर्ण धडा 09 वाचा →

10 🧬 Coverage विरुद्ध mutation

100% line coverage जी 10 पैकी फक्त 4 mutants मारते — coverage काय चालले ते दाखवते, काय तपासले ते नाही.

🧒 सोप्या शब्दांत

कतरिनाला आपला exam paper चांगला आहे का ते जाणून घ्यायचे आहे. 7 प्रश्नांचा तिचा weak paper code ची प्रत्येक line चालवतो: 100%. मग ऐश्वर्या code च्या 10 copies बनवते, प्रत्येकीत एक छोटी पेरलेली चूक. Weak paper त्यातल्या फक्त 4 पकडतो. कडांवरचे प्रश्न असलेला strong paper 9 पकडतो. शेवटची तर खरी चूकच नाही.

📖 नवे शब्दcoverage — tests चालताना code च्या कोणत्या lines चालल्याmutant — एक छोटी पेरलेली चूक असलेली code ची copykilled — test ने mutant ची चूक ओळखली आणि fail झालीequivalent mutant — अगदी तसाच वागणारा बदल, म्हणून कोणतीही test तो पकडू शकत नाही
1📋 coverage: कोणत्या lines चालल्या (RAN)def grade(score): if score < 0 or score > 100: raise ValueError(f"score out of range: {score}") s = round_half_up(score)if s >= 75: return "A"if s >= 60: return "B"if s >= 45: return "C"if s >= 35: return "D" return "F"WEAK suite (7 प्रश्न): 12/12 ओळी = 100%प्रत्येक ओळ चालली — प्रत्येकावर हिरवी पट्टीपण कोणत्या प्रश्नाने त्या खरंच CHECK केल्या का?2🧬 mutation: grade() च्या 10 प्रती, प्रत्येकीत ONE मुद्दाम पेरलेली चूकWEAK · 7STRONG · 21s >= 75→s > 75SURVIVED (टिकला)killed (मारला)s >= 35→s > 35SURVIVED (टिकला)killed (मारला)s >= 60→s >= 61SURVIVED (टिकला)killed (मारला)round_half_up(score)→round(score)SURVIVED (टिकला)killed (मारला)return "A"→return "B"killed (मारला)killed (मारला)return "F"→return "D"killed (मारला)killed (मारला)score < 0 or→score < 0 andkilled (मारला)killed (मारला)s >= 45→s <= 45killed (मारला)killed (मारला)score > 100→score > 101SURVIVED (टिकला)killed (मारला)s >= 75→s > 74SURVIVED (टिकला)SURVIVED (टिकला)killed 4/10killed 9/10दोन्ही suites:100% coverage3♾️ STRONG suite मधला एकमेव टिकलेला mutant EQUIVALENT आहे: s >= 75 आणि s > 74 हे एकच पूर्ण अंक निवडतातs >= 75s > 74·70··71··72··73··74·A75AA76AA77AA78AA79AA80As म्हणजे round_half_up(score): नेहमी पूर्ण अंक — 74.5 कधीच तिथे पोहोचत नाही, म्हणून कोणतीही test हा mutant मारू शकत नाहीcoverage सांगते कोणत्या ओळी चालल्या; mutation testing विचारते की त्या चुकीच्या असत्या तर कोणत्याही test ला ते लक्षात आले असते का
⏪ आधी

Team line coverage वर विश्वास ठेवत असे: tests चालताना प्रत्येक line चालली, तर paper पुरेसा चांगला मानला जाई.

💡 काय

Mutation testing grade() च्या एका copy मध्ये एक छोटी चूक पेरते, ज्याला mutant म्हणतात, आणि कोणती test ती ओळखते ते पाहते.

⚙️ कसे

7 प्रश्नांच्या WEAK suite ला 12/12 line coverage आहे, पण ती 10 पैकी फक्त 4 mutants मारते; 21 ची STRONG suite 9/10 मारते.

🎯 का

Coverage कोणत्या lines चालल्या ते दाखवते, काय तपासले ते नाही. शेवटचा survivor, s > 74, equivalent आहे: कोणतीही test त्याला मारू शकत नाही.

🚀 पुढे

पुढचा धडा code न बदलता pass आणि fail होणाऱ्या tests शोधतो: flaky tests चे हलणारे भिंतीवरचे घड्याळ.

🧪 इथे करून पाहा — प्रश्नपत्रिकेतील प्रश्न निवडा — coverage विरुद्ध मारलेले mutants

संपूर्ण धडा 10 वाचा →

11 🕰️ Flaky tests (लहरी tests)

डुगडुगणारे घड्याळ: वेळ, क्रम, randomness आणि वाट पाहणे — पुन्हा चालवून सापडतात, त्यांना पक्के करून सुधारतात.

🧒 सोप्या शब्दांत

परीक्षा हॉलमधले घड्याळ हलते. बहुतेक दिवस ते बरोबर असते; काही दिवस ते उडी मारते. Flaky test अशीच असते: code बदलला नाही, पण निकाल बदलला. ऐश्वर्या प्रत्येक test 1000 वेळा चालवते. एक 33 वेळा fail होते, फक्त 23:00 नंतर. दुसरी एक list वाटून घेते आणि 6 पैकी 5 क्रमांत fail होते. ती बदलणारी गोष्ट शोधते आणि fixed clock ने ती बांधून ठेवते.

📖 नवे शब्दflaky test — त्याच code वर कधी pass तर कधी fail होणारी testrerun — flake पकडण्यासाठी काहीही न बदलता तीच test अनेकदा चालवणेinjected clock — code ला खरे घड्याळ वाचू न देता ठरलेली वेळ देणे
1🕰️ प्रत्येक test 1000 वेळा चालवा, code मध्ये NO बदल — प्रत्येक लाल खूण म्हणजे अपयशी run12369()डळमळीत गोष्टभिंतीवरचे घड्याळ⏰ वेळ: आज देय, तासाभरात जमापास 967 · नापास 33run 1run 1000→ खरे घड्याळ वाचते (23:00 नंतर नापास)🎲 randomness: 3 unseeded गुण, एकच पासपास 964 · नापास 36run 1run 1000→ unseeded random data⏳ वाट पाहणे: 200 ms sleep, मग job तपासापास 924 · नापास 76run 1run 1000→ काम done होण्याची वाट न पाहता ठरावीक sleepcode कधीच बदलला नाही — फक्त घड्याळ, फासा आणि वेळ बदलली2⏰ 24-तासांच्या डायलवर वेळेचा bug000306091215182123:30 चा run: देय 1 Sep, जमा 00:302 Sep ला → "late" → test नापास1000 पैकी 33 random वेळा 23:00–24:00 मध्ये येतातउपाय: ठरलेले घड्याळ inject करा → पास 1000, नापास 0ठिपके: पहिले 220 runs3🔀 क्रम: एक shared ROLL listरिकामे+Katrina+Dipikaरिकामे+Dipika+Katrina+Katrinaरिकामे+Dipika+Katrina+Dipikaरिकामे+Dipikaरिकामे+Katrina+Dipika+Katrinaरिकामेshared ROLL: 6 पैकी 5 क्रम नापासप्रत्येक test ला नवी ROLL: 0 नापासराखाडी: कधी चाललेच नाही — पहिल्या अपयशावर क्रम थांबला4⏳ 200 ms sleep, मग पाहाsleep 200 ms50400 ms1000 पैकी 76 jobs अजून चालूउपाय: deadline ठेवून 'done' ची वाट पाहाjob वेळ: report ला किती वेळ लागला
⏪ आधी

लाल build हिरवा होईपर्यंत पुन्हा चालवला जाई, आणि त्याच code ने वेगळे उत्तर का दिले हे कोणी विचारत नसे.

💡 काय

Flaky test त्याच code वर कधी pass तर कधी fail होते. ऐश्वर्या प्रत्येक test काहीही न बदलता 1000 वेळा चालवते.

⚙️ कसे

Time 33 वेळा fail (23:00 नंतर), randomness 36, 200 ms sleep 76, आणि shared ROLL वर 6 पैकी 5 test orders fail.

🎯 का

प्रत्येक flake मागे एक बदलणारी गोष्ट असते. ती बांधा: fixed clock ने 1000 passed, 0 failed; seed; 'done' ची वाट पाहा.

🚀 पुढे

पुढच्या धड्यात आधी test लिहिली जाते (red, green, refactor), आणि CI फक्त बदलाला लागणाऱ्या tests चालवते.

🧪 इथे करून पाहा — code न बदलता test पुन्हा चालवा — मग बदलणारी गोष्ट पक्की करा
100001410

संपूर्ण धडा 11 वाचा →

12 🚦 CI, TDD आणि संपूर्ण चित्र

Red, green, refactor; बदलाला लागू असलेलेच tests चालवा — आणि संपूर्ण चित्र.

🧒 सोप्या शब्दांत

एक नवा नियम येतो. कतरिना आधी 5 प्रश्न लिहिते, आणि पाचही fail होतात, कारण अजून code नाही. हे red. दीपिका सर्वात साधा code लिहिते: 5 पैकी 5 pass, green. मग ती तो नीटनेटका करते आणि तरीही 5 पैकी 5 pass. प्रत्येक बदलानंतर CI hall फक्त त्या बदलाला लागणाऱ्या tests चालवतो: 105 पैकी 28. Main मध्ये जाण्यापूर्वी सगळ्या 105 चालतात.

📖 नवे शब्दTDD — test-driven development: आधी fail होणारी test, मग codered · green · refactor — आधी fail, मग सर्वात साध्या code ने pass, मग न मोडता नीटनेटके करणेCI — प्रत्येक बदलावर tests आपोआप चालवणारा मदतनीसtest selection — बदलाचा परिणाम होऊ शकणाऱ्या tests च फक्त चालवणे
1🔴🟢🔵 नव्या नियमासाठी TDD: medical note असल्यास 35 पेक्षा 2 गुण कमी → 35 पर्यंत वाढवा5 प्रश्न, code च्या BEFORE लिहिलेलेnote सह 33 → 35 पर्यंत वाढतोnote सह 34.9 → 35 पर्यंत वाढतोnote शिवाय 33 → 33 च राहतोnote सह 32.9 → 32.9 च राहतोnote सह 35 → 35 च राहतोREDआधी test, code नाहीनापासनापासनापासनापासनापास0/5 पासGREENसर्वात साधा codepasspasspasspasspass5/5 पासREFACTORनियमाला नाव द्याpasspasspasspasspass5/5 पासred सिद्ध करते की test नापास होऊ CAN · green म्हणजे पास होणारी सर्वात साधी गोष्ट · refactor code नीटनेटका करताना 5/5 टिकवतो2🎯 test selection: बदलाचा ज्यांना स्पर्श होतो तेवढेच suites चालवाattendance client मधील बदल → 6 पैकी 3 suites, 105 पैकी 28 tests40unit/grade25unit/report_card18unit/attendance12integ/store6contr/attendance_pact4e2e/report_flowgrade() मधील बदल → 6 पैकी 3 suites, 105 पैकी 69 tests4025181264main मध्ये merge करताना सगळ्या 105 चालतातच — selection loop वेगवान करते, पूर्ण run ची जागा कधीच घेत नाही3🚦 CI: जलद checks आधीlintसेकंदunitintegrationcontracte2eमिनिटेपहिल्या लाल वर थांबा4🧭 संपूर्ण चित्रप्रश्न→योग्य प्रश्न→कोऱ्या उत्तरपत्रिका→stand-ins (बदली)→खरा database→वचने→the pyramid→properties→mutants→स्थिर घड्याळ→CIप्रत्येक धडा म्हणजे code बद्दल एक प्रश्न — मग प्रश्नांबद्दलचे प्रश्न
⏪ आधी

आधी code येत असे आणि tests नंतर, कधी कधी तर नाहीच; प्रत्येक बदलावर एकतर सगळ्या 105 tests चालत किंवा एकही नाही.

💡 काय

TDD: आधी 5 tests लिहा (red, 0/5), मग सर्वात साधा code (green, 5/5), मग तो नीटनेटका करा (refactor, तरीही 5/5).

⚙️ कसे

Test selection: attendance client मधील बदल 6 पैकी 3 suites, 105 पैकी 28 tests चालवतो; grade() मधील बदल 69 चालवतो.

🎯 का

Red सिद्ध करते की test fail होऊ शकते; CI lint → unit → integration → contract → e2e चालवते, पहिल्या red ला थांबते, main वर सगळ्या 105.

🚀 पुढे

पुढे कुठे: CI/CD school या tests खऱ्या pipelines मध्ये चालवते, आणि API school contracts design करते.

🧪 इथे करून पाहा — नवा नियम टप्प्याटप्प्याने TDD करा; मग बदल कशाला स्पर्श करतो ते निवडा
2

संपूर्ण धडा 12 वाचा →

धडा 01 सुरू करा → 🗓️ अभ्यास योजना (4 आठवडे) 📐 सर्व 12 धड्यांच्या आकृत्या 🧪 प्रश्नमंजुषा (16 प्रश्न) ⏮️ आधी काय होते & फायदे-तोटे