How do I mock dependencies in EK9 tests without Mockito?
← Testing · Ref: Q808
EK9 does not need Mockito, unittest.mock, or any mocking library. Instead, you use traits and dependency injection to create test doubles at the language level.
THE PATTERN
1. Define behaviour as a trait (interface)
2. Production code depends on the trait, not the concrete implementation
3. In tests, provide a test implementation of the trait
4. DI wiring in the application block selects which implementation to use
WHY NO MOCKING FRAMEWORK
Mocking frameworks exist because languages allow tight coupling to concrete classes. EK9 prevents this:
- Components depend on traits (interfaces), not concrete types
- The DI system wires implementations at compile time
- Test implementations are just another implementation of the trait
- No reflection, no bytecode manipulation, no proxy objects
TRAIT-BASED TEST DOUBLES
Define a trait:
DataStore as trait fetch() as pure abstract -> key as String <- rtn as String?
Production implementation:
RealDataStore is DataStore override fetch() as pure -> key as String <- rtn as String: lookupDatabase(key)
Test implementation:
FakeDataStore is DataStore override fetch() as pure -> key as String <- rtn as String: "test-value"
TEST APPLICATION
Wire the test double in a test application block:
defines application TestApp register FakeDataStore() as DataStore
ADVANTAGES
- Compile-time verified: missing method implementations caught immediately
- No magic: test doubles are normal classes implementing traits
- Reusable: same test double works across all tests
- Type-safe: cannot accidentally mock the wrong method signature
See Q227 for DI basics. See Q324 for DI deep dive. See Q155 for basic testing.
Example
defines module qa.testdeep.mocking defines trait <?- The trait defines the contract. Production and test code both implement this. -?> Formatter format() as pure abstract -> number as Integer <- rtn as String? defines class // === PRODUCTION IMPLEMENTATION === FancyFormatter with trait of Formatter override format() as pure -> number as Integer <- rtn as String: `[${number}]` // === TEST DOUBLE === SimpleFormatter with trait of Formatter override format() as pure -> number as Integer <- rtn as String: $number defines program MockingDemo() stdout <- Stdout() // Production: uses FancyFormatter fancy <- FancyFormatter() stdout.println(fancy.format(42)) // Test: uses SimpleFormatter (the "mock") simple <- SimpleFormatter() stdout.println(simple.format(42)) // Both implement Formatter trait // DI wiring chooses which one: // Production app: register FancyFormatter() as Formatter // Test app: register SimpleFormatter() as Formatter stdout.println("No Mockito needed") stdout.println("Traits replace mocking frameworks")
Common mistakes
E50020 — A class implements a trait with 'with trait of', not 'extends' — using 'extends' on a trait is an incompatible genus (class vs trait). See ek9 -h E50020 for details.
Incorrect:
FancyFormatter extends Formatter
Correct:
FancyFormatter with trait of Formatter
Other ways to ask this
- What is the EK9 equivalent of Mockito?
- How do I use traits for test doubles in EK9?
- How do I test components with injected dependencies in EK9?
Coming from another language?
Java: Mockito mock()/when()/verify(), Spring @MockBean. Python: unittest.mock.patch(), MagicMock. Rust: mockall crate with #[automock]. Go: interfaces + hand-written fakes (standard pattern). Kotlin: MockK or Mockito-kotlin. EK9: trait implementations as test doubles, DI wiring selects implementation, no mocking library needed.
Keywords: double, mock, stub, interface, trait, dependency, test, fake, injection, mockito