You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Refactor SystemContext to use interfaces instead of open classes
Constructor parameters in open classes forced test subclasses to provide
dummy dependencies (DataSource, Config) even when fakes never used them.
This became a real problem as repositories evolved to need more dependencies.
Changes:
- SystemContext: Use interfaces with anonymous objects for dependency groups
- SystemTestContext: Implement typed test pattern with dual access paths
- Tests updated to use testRepositories/testClients for fake-specific methods
- Documentation expanded to explain why open classes didn't work
- Skills README enhanced with detailed skill descriptions
- Parent README updated with skills section
Benefits:
- No constructor parameters to satisfy in tests
- No casting needed to access fake methods
- Type-safe dual access: repositories (interface) vs testRepositories (concrete)
- Production wiring stays in one place (anonymous objects)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
overrideval applicationRepo =ApplicationRepositoryFake() // Doesn't even use dataSource!
56
+
}
57
+
```
58
+
59
+
This became a real problem in production code when repositories started needing database connections, configuration objects, or HTTP clients in their constructors. Test code had to create dummy instances of these just to satisfy the superclass constructor, even though fakes are in-memory and never touch databases or HTTP.
60
+
61
+
**The interfaces solution:**
62
+
- No constructor parameters
63
+
- No dummy objects needed in tests
64
+
- Test implementations must explicitly provide every dependency (no hidden inherited behavior)
65
+
- Production wiring stays in one place (anonymous objects inside SystemContext)
66
+
67
+
### Typed Test Implementations Pattern
68
+
69
+
The test context provides **two access paths** using Kotlin's covariant return types:
- I spend less time on Google figuring out which annotation or XML/JSON/YAML element to specify.
27
101
- It shows the different places I am injecting each object/service/repo/client.
28
102
- It makes it possible to inject whatever I want, whenever I want. Think Test Doubles like Fakes. Or even once in a while Mocks.
29
-
- I TDD a lot more. Because I control how much is being loaded and when, I only load what’s necessary and it’s fast. I can decide to make the feedback loop efficient even if I am writing automated tests that do actual HTTP and database calls.
103
+
- I TDD a lot more. Because I control how much is being loaded and when, I only load what's necessary and it's fast. I can decide to make the feedback loop efficient even if I am writing automated tests that do actual HTTP and database calls.
30
104
- I re-factor more. Sometimes when I re-factor, the specific patterns of the framework get in the way. Without framework annoyances, it is easier and more fun to do refactorings.
105
+
- No casting needed in tests (typed test implementations give you concrete fake types)
106
+
- Test contexts are lightweight - fresh context per test prevents state leakage
31
107
32
108
# Related Reading
33
109
-[Rolling your own dependency injection](https://anderssv.medium.com/rolling-your-own-dependency-injection-7045f8b64403)
0 commit comments