Mota — Testing Strategy¶
SDLC phase: Implementation (Chapter 5.2)
Purpose: What gets tested, how, and what "release-ready" means.
Governing principle: test effort follows trust. The interval engine and the sync queue get near-total coverage because a failure there destroys the product's only real asset — the user's belief that the record is true. The settings screen gets almost none because a failure there is annoying, not fatal.
1. Test pyramid¶
┌─────────────┐
│ Manual │ Exploratory + UAT
│ (human) │ ~5% of effort
├─────────────┤
│ E2E │ 8–10 critical journeys
│ (Playwright│ ~15% of effort
│ /Detox) │
├─────────────┤
│ Integration │ Sync, payments, RLS, notifications
│ │ ~30% of effort
├─────────────┤
│ Unit │ Interval engine, calculations, formatting
│ │ ~50% of effort
└─────────────┘
2. Unit testing¶
Coverage target: ≥70% overall; 100% on the interval engine.
| Module | What is tested | Why it matters |
|---|---|---|
| Interval engine | All 10 cases in 05-solution-design.md §4.5, plus property-based tests |
A wrong reminder is worse than no reminder |
| Fuel consumption | km/l across irregular fill-ups, partial fills, missed odometer | Wrong numbers destroy credibility instantly |
| Cost aggregation | Totals, cost/km, period filtering, deleted-record exclusion | Users will check the maths |
| Date/interval arithmetic | Month-end edge cases (31 Jan + 1 month), leap years, timezone | Classic source of silent bugs |
| Currency formatting | USD/ZiG display, rounding, thousands separators | A misplaced decimal on money is unforgivable |
| Sync conflict resolution | Last-write-wins per field, monotonic odometer | Data integrity |
The interval engine is tested as a pure function. No mocking, no database, no time dependency — today is a parameter. This is why the design separates it from I/O.
Property-based tests worth writing:
km_remainingis never NaN and never computed from a negative interval.- For any record set,
due_odois always ≥ the most recent service odometer. - Classification is monotonic: if km_remaining increases, status can never escalate.
3. Integration testing¶
| Area | Test | Pass condition |
|---|---|---|
| Offline sync | Queue 50 writes offline, reconnect, force a mid-sync crash, restart | All 50 apply exactly once; no duplicates; no losses |
| Idempotency | Replay the same client_id 10 times |
One row created; 9 no-ops; all return success |
| RLS — user isolation | Auth as A, query B's vehicle/records by ID | Empty result or 403 — never data |
| RLS — garage isolation | Auth as garage X, query garage Y's leads | Empty result |
| PII gate | Auth as garage, query lead without contact_released_at |
Phone number is null |
| Payment webhook | Replay a Paynow webhook 5 times | Invoice marked paid once; no duplicate credit |
| Payment webhook | Forged signature | Rejected 401, no state change |
| Reminder scheduler | Seed due items, run the job twice in a day | One notification per stage, not two |
| Reference data change | Edit an interval, trigger recompute | Affected vehicles' due dates update; others untouched |
The RLS tests are a release blocker. A data-isolation defect is a CDPA incident, not a bug.
4. End-to-end testing¶
Eight journeys, run against a staging environment with a seeded database.
| # | Journey | Critical assertion |
|---|---|---|
| E2E-1 | New user: OTP → add vehicle → see dashboard | Completes in <60s; dashboard shows correct interval |
| E2E-2 | Log a service offline → reconnect | Record appears; due date recalculates |
| E2E-3 | Log 3 fill-ups | km/l displays correctly from the third |
| E2E-4 | Generate and share PDF | PDF renders with all records, correct totals, QR present |
| E2E-5 | Reminder → garage request → lead appears in console | Lead visible in <60s with correct service and contact status |
| E2E-6 | Garage marks lead booked → invoice includes the fee | Fee appears once, at the right amount |
| E2E-7 | Garage pays via Paynow sandbox | Invoice marked paid; reconciliation matches |
| E2E-8 | User deletes account | PII purged within the flow; garage-side billing record anonymised |
5. Device and environment matrix¶
| Dimension | Coverage | Reason |
|---|---|---|
| Reference low-end device | Tecno/Itel-class, 2GB RAM, Android 10 | The NFR baseline. Every performance and usability test runs here first. |
| Modern mid-range | 4GB RAM, Android 13 | Common upgrade path |
| Oldest supported | Android 8.0 (API 26) | Contract floor |
| Network: 3G | Throttled 400kbps, 300ms latency | Real-world baseline for the target market |
| Network: offline | Airplane mode | Core requirement |
| Network: flaky | 30% packet loss, intermittent | Realistic rural/suburban reality |
| Font scaling | 100% and 200% | Accessibility |
| Throttled CPU | 4× slowdown | Simulates cheap silicon |
Not covered in v1: iOS devices (out of scope), tablets (supported incidentally), desktop browsers for the consumer app.
6. User acceptance testing (UAT)¶
Run at the end of Gate 1 and again at Gate 3.
| Participant group | Count | Tasks |
|---|---|---|
| First-time owners (never seen the app) | 10 | Add a vehicle; log a service; find what's due; export PDF |
| Existing users (2+ weeks of use) | 10 | Log 3 fill-ups; interpret a reminder; request a garage |
| Garage owners | 5 | Register a listing; respond to a lead; submit a price; find the invoice |
UAT pass criteria:
| Task | Pass threshold |
|---|---|
| Add a vehicle unaided | ≥9/10 |
| Log a service in <30s | ≥8/10 |
| Correctly explain what "due in 900km" means | ≥9/10 |
| Export and share a PDF unaided | ≥8/10 |
| Garage responds to a lead unaided | ≥5/5 |
Any task below threshold blocks release. UAT failure is a design failure, not a user failure.
7. Validation vs verification¶
CUT distinguishes these, and the distinction is genuinely useful. Both apply here.
Verification — "did we build it right?"¶
| Check | Method | Owner |
|---|---|---|
| Requirements met | Traceability from FR → test → pass | Builder |
| NFRs met | Reference-device measurements, bundle size, session data | Builder |
| Code quality | Review of every PR; no unreviewed merges | Builder |
| Security | RLS test suite, dependency audit, secret scanning | Builder |
| Regression | Full suite green before every release | CI |
Validation — "did we build the right thing?"¶
| Question | Measure | Threshold | When |
|---|---|---|---|
| Do users keep the record? | 30-day retention | ≥25% | Gate 1 |
| Do reminders get acted on? | Acknowledge/open rate | ≥40% | Gate 2 |
| Do garages get value? | Lead → booked conversion | ≥30% | Gate 2 |
| Do garages pay? | Paying garages | ≥5 | Gate 3 |
| Is the price index useful? | Views per active user | ≥1.5/mo | Gate 3 |
| Would users be upset if it disappeared? | Exit survey: "very disappointed" | ≥40% (Sean Ellis test) | Gate 3 |
Verification can be fully automated. Validation cannot. Scheduling one is engineering; scheduling the other is the actual job.
8. Release criteria¶
A build ships to production only when all of the following are true:
| # | Criterion | Blocking |
|---|---|---|
| R1 | All unit and integration tests pass in CI | Yes |
| R2 | All 8 E2E journeys pass on the reference device | Yes |
| R3 | RLS isolation suite passes | Yes — hard blocker |
| R4 | No P1 or P2 defects open | Yes |
| R5 | Bundle size within budget (<200KB gzipped) | Yes |
| R6 | Reference-device cold start <3s | Yes |
| R7 | Crash-free sessions >99% in staging soak test | Yes |
| R8 | Privacy policy and deletion flow verified | Yes before public launch |
| R9 | Data controller registration confirmed | Yes before public launch |
| R10 | Rollback path tested | Yes |
9. What we deliberately don't test¶
| Not tested | Reason |
|---|---|
| iOS | Out of scope |
| Payment processor internals | Trust the provider; test our integration and webhook handling |
| ZINARA data accuracy | No integration exists; user-entered data is the contract |
| OTP delivery across all networks continuously | Tested at Gate 0 and spot-checked monthly; cannot be automated meaningfully |
| 100% code coverage | Diminishing returns; coverage follows trust, not a number |
| Load testing at scale | At 10,000 users the system is nowhere near capacity; test at a plausible 10× and move on |
10. Test data policy¶
- Production data is never copied into staging.
- Staging uses generated vehicles, records, and garages.
- Phone numbers in tests use reserved ranges; no real contact attempts.
- Payment tests use Paynow sandbox only.
- Any screenshot or demo uses synthetic data — a real user's service history in a screenshot is a CDPA incident.