Record being wrong about which change broke a test
A UI test went red in the same run that adopted three new APIs. The one touching that screen was reverted; the test still failed. A modifier order was corrected; still failed. Simulator data was wiped; still failed. Running it against a clean checkout — which should have been the first move, not the fourth — it failed there too, with none of the work present. The scrolling entry had already been rewritten to explain the fault in confident detail. That write-up was fiction, so it now says what actually happened: the API was convicted on circumstance and remains unproven here, and the one claim that survives is about its two-way binding costing invalidations. xcode-practice gets the general lesson, since it will outlive this particular test: stash and rerun before reasoning about which edit did it, and a note recording a false cause is worse than no note, because the next person believes it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f5a3d932fa
commit
542dd925d0
4 files changed
+46
-5
No files matched your search
@@ -12,7 +12,7 @@
|
|||||||
"name": "pdat-swift-practice",
|
"name": "pdat-swift-practice",
|
||||||
"source": "./pdat-swift-practice",
|
"source": "./pdat-swift-practice",
|
||||||
"description": "Curated Apple platform findings with sources, plus practices learned from production incidents.",
|
"description": "Curated Apple platform findings with sources, plus practices learned from production incidents.",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "PDAT s.r.o."
|
"name": "PDAT s.r.o."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pdat-swift-practice",
|
"name": "pdat-swift-practice",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"description": "Curated Apple platform findings with sources, plus practices learned from production incidents.",
|
"description": "Curated Apple platform findings with sources, plus practices learned from production incidents.",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "PDAT s.r.o.",
|
"name": "PDAT s.r.o.",
|
||||||
|
|||||||
@@ -25,9 +25,29 @@ following the offset while the user drags.
|
|||||||
to reach `proxy.scrollTo` from a `.task`. In our card list that is the
|
to reach `proxy.scrollTo` from a `.task`. In our card list that is the
|
||||||
filter chip strip, which scrolls the active chip into view.
|
filter chip strip, which scrolls the active chip into view.
|
||||||
|
|
||||||
**Step forward?** Modest but real. One layer of nesting disappears and the
|
**Step forward? Unproven — we backed it out.** Swapping `ScrollViewReader`
|
||||||
scroll position stops being write-only. Not urgent — the existing code
|
for `.scrollPosition(id:)` coincided with the UI test for that strip
|
||||||
works — so worth doing when that view is touched for another reason.
|
failing, so it looked like the culprit and was reverted. It was not:
|
||||||
|
the same test fails on a completely clean tree, with none of the change
|
||||||
|
present. Something in the environment broke that test, and the swap was
|
||||||
|
convicted on circumstance.
|
||||||
|
|
||||||
|
The revert stands anyway, because it was never worth much here — this
|
||||||
|
strip only ever *writes* a scroll target, which is exactly what the proxy
|
||||||
|
already does. But the reasoning above is worth keeping, because being
|
||||||
|
wrong about it taught the real lesson: **when a change and a failure
|
||||||
|
arrive together, check the failure against an unchanged tree before
|
||||||
|
concluding.** Two rounds of edits went into explaining a fault that was
|
||||||
|
not there.
|
||||||
|
|
||||||
|
One thing that does hold, independent of all that:
|
||||||
|
|
||||||
|
- **`scrollPosition` is two-way, so it costs invalidations.** It writes to
|
||||||
|
its binding on every scroll, where the proxy only takes commands. It
|
||||||
|
therefore belongs in a small view. Ours would have sat in a screen whose
|
||||||
|
body derives a sorted, grouped card list — every drag of the strip would
|
||||||
|
have recomputed all of it. Extracting the strip into its own view was
|
||||||
|
worth keeping regardless of which scrolling API ends up inside it.
|
||||||
|
|
||||||
**Available since.** iOS 17, so nothing blocks it at our deployment
|
**Available since.** iOS 17, so nothing blocks it at our deployment
|
||||||
target. `onScrollGeometryChange` needs iOS 18.
|
target. `onScrollGeometryChange` needs iOS 18.
|
||||||
|
|||||||
@@ -136,6 +136,27 @@ dead when it is passing. Also: do not pipe a test run through `tail` or a
|
|||||||
narrow `grep` and then diagnose from what survived — filter the saved log
|
narrow `grep` and then diagnose from what survived — filter the saved log
|
||||||
afterwards instead.
|
afterwards instead.
|
||||||
|
|
||||||
|
## Check a failure against an unchanged tree before blaming the change
|
||||||
|
|
||||||
|
A UI test started failing in the same run that introduced three API
|
||||||
|
changes. The obvious suspect — the one touching that exact screen — was
|
||||||
|
reverted, and the test still failed; a modifier order was corrected, still
|
||||||
|
failed; the simulator's app data was wiped, still failed. Only then was
|
||||||
|
the test run against a clean checkout with none of the work present, and
|
||||||
|
it failed there too. The change had been innocent all along, and two
|
||||||
|
rounds of edits went into explaining a fault that did not exist.
|
||||||
|
|
||||||
|
`git stash` and one run is cheaper than any amount of reasoning about
|
||||||
|
which edit did it. Do it first, not fourth. The same goes for what gets
|
||||||
|
written down afterwards: a note recording a false cause is worse than no
|
||||||
|
note, because the next person believes it.
|
||||||
|
|
||||||
|
Corollary for UI tests: when one goes red without a code change, suspect
|
||||||
|
what the test depends on but does not control — accumulated seed data,
|
||||||
|
`@AppStorage` surviving between launches, a simulator location and the
|
||||||
|
"nearby" sort order it drives, or an element scrolled out of a lazy
|
||||||
|
container and therefore missing from the accessibility tree entirely.
|
||||||
|
|
||||||
## Core Data in tests
|
## Core Data in tests
|
||||||
|
|
||||||
Build objects through the context's own model rather than
|
Build objects through the context's own model rather than
|
||||||
|
|||||||
Reference in new issue
Block a user