0
0
Android Kotlinmobile~15 mins

Repository testing with fakes in Android Kotlin - Deep Dive

Choose your learning style9 modes available
Overview - Repository testing with fakes
What is it?
Repository testing with fakes means checking if the part of your app that gets and saves data works correctly by using simple pretend versions of data sources. Instead of using real databases or network calls, you use fake ones that behave like the real ones but are easier to control. This helps you find mistakes early without needing the full app or internet connection.
Why it matters
Without testing repositories properly, apps can have hidden bugs that cause wrong data to show or crashes when saving information. Using fakes makes tests faster and more reliable because they don't depend on slow or unstable real services. This means developers can fix problems quickly and deliver better apps that users trust.
Where it fits
Before learning repository testing with fakes, you should understand basic Kotlin programming, how repositories work in app architecture, and simple unit testing. After this, you can learn about mocking frameworks, integration testing, and testing with real databases or network calls.
Mental Model
Core Idea
Repository testing with fakes means replacing real data sources with simple pretend ones to check if data handling works correctly without outside dependencies.
Think of it like...
It's like practicing a recipe using plastic food models instead of real ingredients to make sure you follow the steps right before cooking for real.
┌───────────────┐
│   Repository  │
├───────────────┤
│  Uses DataSrc │
│  (Real or Fake)│
└──────┬────────┘
       │
┌──────▼───────┐      ┌───────────────┐
│  Real Source │      │   Fake Source │
│ (DB, Network)│      │ (In-memory)   │
└──────────────┘      └───────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Repository Role
🤔
Concept: Learn what a repository does in app architecture.
A repository is a middleman between your app and data sources like databases or web services. It hides where data comes from and how it's saved, so the rest of your app doesn't worry about those details.
Result
You know that repositories organize data access and make your app code cleaner.
Understanding the repository's role helps you see why testing it separately is important to catch data-related bugs early.
2
FoundationBasics of Unit Testing in Kotlin
🤔
Concept: Learn how to write simple tests for Kotlin functions.
Unit tests check small parts of your code to make sure they work as expected. In Kotlin, you write test functions that call your code and compare results to expected values using assertions.
Result
You can write and run tests that confirm your code behaves correctly.
Knowing unit testing basics is essential before testing repositories because repositories are tested with these methods.
3
IntermediateWhat Are Fakes in Testing
🤔Before reading on: do you think fakes are the same as mocks or different? Commit to your answer.
Concept: Fakes are simple, working implementations of interfaces used in tests instead of real components.
Fakes behave like real data sources but are simpler and faster. For example, a fake database might just store data in a list in memory. Unlike mocks, which only check if methods were called, fakes actually store and return data.
Result
You can replace real data sources with fakes in tests to simulate real behavior without complexity.
Understanding fakes helps you write more realistic tests that catch real logic errors, not just method calls.
4
IntermediateCreating a Simple Fake Data Source
🤔Before reading on: do you think a fake data source needs to connect to a real database? Commit to yes or no.
Concept: Learn how to build a fake data source that stores data in memory for testing.
Create a class that implements the data source interface but uses a simple list to save and return data. This class has no real database or network calls, making tests fast and reliable.
Result
You have a working fake data source that can be used in repository tests.
Knowing how to build fakes empowers you to control test data precisely and avoid flaky tests.
5
IntermediateInjecting Fakes into Repository Tests
🤔Before reading on: do you think repositories should create their own data sources inside tests? Commit to yes or no.
Concept: Learn to pass fake data sources into repositories during tests to isolate repository logic.
Instead of letting the repository create real data sources, pass the fake ones as parameters. This way, tests control exactly what data the repository sees and how it behaves.
Result
Repository tests run using fakes, making them fast, predictable, and independent of real services.
Injecting fakes improves test isolation and helps find bugs in repository code without external noise.
6
AdvancedTesting Repository Behavior with Fakes
🤔Before reading on: do you think testing with fakes can catch logic errors in repositories? Commit to yes or no.
Concept: Use fakes to verify that repositories correctly handle data operations like saving, updating, and retrieving.
Write tests that add data through the repository, then check if the fake data source contains the expected data. Also test edge cases like empty data or errors simulated by the fake.
Result
You confirm that repository methods behave correctly in various scenarios without real data sources.
Testing repository behavior with fakes ensures your app's data layer works correctly before integrating with real services.
7
ExpertAdvanced Fake Design and Limitations
🤔Before reading on: do you think fakes perfectly replace real data sources in all tests? Commit to yes or no.
Concept: Explore how to design fakes that mimic complex behaviors and understand their limits.
Advanced fakes can simulate delays, errors, or partial data to test repository resilience. However, fakes can't fully replace real databases or network conditions, so integration tests are still needed.
Result
You can build sophisticated fakes for thorough repository testing but know when to switch to real components.
Knowing fake design tradeoffs helps balance fast unit tests with realistic integration tests for robust apps.
Under the Hood
When testing with fakes, the repository interacts with a substitute data source that implements the same interface as the real one. This fake stores data in memory and returns it synchronously, avoiding real I/O operations. The Kotlin compiler and runtime treat fakes like any other object, so repository code runs unchanged but against controlled data.
Why designed this way?
Fakes were created to speed up tests and remove dependencies on slow or unreliable external systems. They provide a lightweight, deterministic environment for testing business logic. Alternatives like mocks only check interactions, while fakes provide actual data handling, making tests more meaningful.
┌───────────────┐
│   Repository  │
├───────────────┤
│ Calls methods │
│ on DataSource │
└──────┬────────┘
       │
┌──────▼─────────────┐
│  Fake Data Source   │
│ - Stores data in    │
│   memory            │
│ - Returns data fast │
└────────────────────┘
Myth Busters - 3 Common Misconceptions
Quick: do you think fakes are only used to check if methods were called? Commit yes or no.
Common Belief:Fakes are just like mocks and only verify if functions were called.
Tap to reveal reality
Reality:Fakes provide a working implementation that stores and returns data, unlike mocks which only check calls.
Why it matters:Confusing fakes with mocks leads to shallow tests that miss real logic errors in data handling.
Quick: do you think testing with fakes removes the need for any real database tests? Commit yes or no.
Common Belief:If you test with fakes, you don't need to test with real databases or network calls.
Tap to reveal reality
Reality:Fakes speed up unit tests but can't replace integration tests with real components to catch environment-specific bugs.
Why it matters:Skipping real integration tests can let critical bugs slip into production that only appear with real data sources.
Quick: do you think fakes must perfectly mimic every detail of the real data source? Commit yes or no.
Common Belief:Fakes have to exactly replicate all behaviors of real data sources to be useful.
Tap to reveal reality
Reality:Fakes only need to mimic enough behavior to test repository logic; too much complexity defeats their purpose.
Why it matters:Overcomplicating fakes makes tests slow and fragile, losing the benefits of fast, simple testing.
Expert Zone
1
Fakes can be designed to simulate network latency or errors, allowing testing of repository error handling without real network calls.
2
Using fakes encourages designing repositories with clear interfaces and separation of concerns, improving code maintainability.
3
Fakes can sometimes hide bugs that only appear with real databases due to differences in transaction handling or concurrency.
When NOT to use
Avoid relying solely on fakes when testing complex database queries, concurrency, or real network conditions. Use integration tests with real databases or network mocks for those cases.
Production Patterns
In production apps, fakes are used in unit tests to verify repository logic quickly. Integration tests run separately with real data sources. Continuous integration pipelines run both to ensure code quality before release.
Connections
Dependency Injection
Repository testing with fakes builds on dependency injection by passing fake data sources into repositories.
Understanding dependency injection helps you see how to swap real components with fakes easily for testing.
Test-Driven Development (TDD)
Using fakes in repository tests supports TDD by enabling fast, isolated tests that guide code design.
Knowing how fakes speed up tests helps you practice TDD effectively in mobile development.
Simulation in Engineering
Fakes in testing are like simulations in engineering that replace real parts with models to test systems safely and cheaply.
Recognizing this connection shows how testing fakes are a practical way to reduce risk and cost in software development.
Common Pitfalls
#1Using real database in unit tests causing slow and flaky tests.
Wrong approach:val repo = UserRepository(RealDatabase()) @Test fun testSaveUser() { repo.saveUser(User("Alice")) assertTrue(repo.getUser("Alice") != null) }
Correct approach:val repo = UserRepository(FakeDatabase()) @Test fun testSaveUser() { repo.saveUser(User("Alice")) assertTrue(repo.getUser("Alice") != null) }
Root cause:Not isolating repository from real data sources leads to slow tests dependent on external systems.
#2Creating fakes inside repository instead of injecting them, making tests hard to control.
Wrong approach:class UserRepository { private val dataSource = FakeDatabase() fun saveUser(user: User) { dataSource.save(user) } }
Correct approach:class UserRepository(private val dataSource: DataSource) { fun saveUser(user: User) { dataSource.save(user) } } // In test: val fake = FakeDatabase() val repo = UserRepository(fake)
Root cause:Tight coupling between repository and data source prevents swapping fakes in tests.
#3Making fakes too complex, replicating full database logic unnecessarily.
Wrong approach:class FakeDatabase : DataSource { // Complex SQL parsing and transaction simulation }
Correct approach:class FakeDatabase : DataSource { private val storage = mutableListOf() override fun save(user: User) { storage.add(user) } override fun get(name: String) = storage.find { it.name == name } }
Root cause:Trying to perfectly mimic real data sources defeats the purpose of simple, fast fakes.
Key Takeaways
Repository testing with fakes replaces real data sources with simple, controllable substitutes to test data handling logic quickly and reliably.
Fakes provide actual data storage and retrieval behavior, unlike mocks that only check method calls, making tests more meaningful.
Injecting fakes into repositories improves test isolation and helps catch bugs early without relying on slow or unstable external systems.
While fakes speed up unit tests, integration tests with real databases or network calls are still necessary to catch environment-specific issues.
Designing effective fakes balances simplicity and realism to keep tests fast and useful without overcomplicating the test setup.