· 6 min · #unity #architecture #testing
Bloom & Place devlog #12: interface contracts and the test plan
The interfaces two programmers agreed on before either wrote any code, plus the test plan that turns the architecture claim into a measurable outcome.
Why interface contracts exist on this project
Bloom & Place is being built by two programmers, a gameplay programmer (me) and a visual programmer. Without contracts, that splits two ways. Either we serialise the work and one waits while the other ships, or we both build against assumptions and find out at integration that they didn’t match.
Interface contracts kill that. Each interface is a typed agreement. Whoever implements it exposes these members, and whoever consumes it can rely on them. Implementer and consumer can develop in parallel. Neither needs to read the other’s code.
This is the same DI argument I made in devlog #4, applied at the team-process level. DI buys parallel development. Interface contracts are the document that makes the parallelism real.
The four contracts
event OnPlantClicked
event OnPlantSelected
event OnPlantDissatisfied(PlantData)
event OnPlantRemoved(Plant)
event OnLevelFailed
Three observations from the contracts:
- Every contract is event-shaped, not method-shaped. All four interfaces expose events. Only
IPlantSelectableexposes a single method. The visual programmer reacts to things; they don’t drive things. That asymmetry is the contract enforcing the layer hierarchy from devlog #10. - The visual programmer never sees concrete gameplay types. They subscribe to events that pass
PlantDataorPlantreferences, but only as read-only lookups, never as objects to mutate. The contract is one-directional information flow. - Adding a new contract is the only way to add a cross-team dependency. If gameplay needs visual to do something, it goes through this document first. Ad-hoc method calls between team boundaries are not allowed.
Testing, the actual research output
Two passes:
1. Functional tests
Black-box test cases verifying each system does what its spec says. Sample:
| ID | System | Scenario | Expected output | Precondition | Status |
|---|---|---|---|---|---|
| TC-01 | PlacementSystem | Plant placed in a valid slot | Plant moves to slot; OnPlantPlaced published | Slot empty and valid | Pass / Fail |
| TC-02 | SatisfactionSystem | Matching plant in matching slot | SatisfactionScore = 1.0; OnPlantSatisfied published | Plant placed in slot matching its preference | Pass / Fail |
| TC-03 | SatisfactionSystem | Mismatched plant in mismatched slot | SatisfactionScore = 0.0; OnPlantDissatisfied published | Plant placed in slot not matching preference | Pass / Fail |
| TC-04 | LevelSystem | All plants satisfied | OnLevelComplete published; scene transitions to next level | All slots filled and all plants satisfied | Pass / Fail |
| TC-05 | Event flow | OnPlantSatisfied is received by VisualFeedbackSystem | Happy animation plays after event received | Plant placed at preferred position | Pass / Fail |
These verify the systems work; they don’t verify the architecture is loose. That’s the next pass.
2. Coupling analysis, the real test
The thesis claim is empirical. VContainer plus EDA produces lower coupling than the conventional Unity approach. To prove it, I need two implementations of the same gameplay and a side-by-side measurement.
The procedure, written down so it’s reproducible:
- Build a class diagram for each version (non-DI baseline + VContainer/EDA target).
- For every system class, count Ce. Only count external types that aren’t Unity built-in, primitive, or generic.
- For every system class, derive Ca by inverting the dependency graph.
- Compute Instability with
I = Ce / (Ce + Ca). - Tabulate side-by-side and look for the deltas.
The win condition was set in devlog #2 and pinned in devlog #7:
- Aggregate Ce on domain classes drops meaningfully.
- Specific high-Ce classes called out in the baseline (
Plant,ScoreManager) hit their targets from the after-table. - Zero SDP violations involving core domain classes.
ScoreManager’s I drops toward the low end of the manager range.
If the metrics don’t move, that’s the result, and I publish it. Devlog #2 already contains the pre-image of every cell I’ll be filling in. There’s no version of this where I get to quietly redefine “win.”
Design phase, done
Five design artefacts were produced in this phase:
- Layer hierarchy (devlog #10).
- Dependency graph, before and after (devlog #10).
- VContainer binding scheme (devlog #11).
- Event flow (devlog #11).
- Interface contracts + test plan (this post).
Waterfall says I now move to Implementation with these artefacts as fixed inputs. If implementation discovers a real gap, I’ll document the design revision openly rather than rewriting these posts to make the gap disappear.
Next
Devlog #13 is the first lines of production code. GameLifetimeScope, the first interface seam, and the first unit test that runs without a Unity scene.