Testing & QA
⚠️ Reality check (2026-06): The mobile consolidation removed the Hilt-based
test-coremodule. Post-consolidation, client test coverage is near-zero (sharedhas ~4 test files for 491 sources;composeApp0/287;androidApp0/2). Only theservermodule has meaningful coverage (~53 files). Testing infrastructure is undergoing a rebuild — see tech-debt/testing.md for the current gap. This document describes the target state; consult the tech-debt register for the actual state.
This document describes the testing strategy and quality assurance practices for Oter.
Overview
Testing layers, current coverage reality, and aspirational targets.
Testing Philosophy
Current priority (2026-06): Rebuild client test coverage from near-zero to meaningful levels. The Hilt-based
test-coremodule was removed during mobile consolidation; akoin-testreplacement is being established.
Oter follows a pragmatic testing approach focused on:
- Critical paths: Balance prediction engine and financial calculations
- User workflows: End-to-end user journeys
- Data integrity: Financial data accuracy
- Cross-platform consistency: Shared code behavior across platforms
Current Testing Structure
Unit Tests
Location: */src/test/ directories
Coverage Areas:
- Business logic in service layers
- Utility functions
- Data transformations
- Calculation algorithms
Example Test Structure:
class TransactionServiceTest {
@Test
fun `calculate starting balance correctly`() {
// Arrange
val account = Account(initialBalance = 1000.0)
val transactions = listOf(
Transaction(amount = 100.0, type = INCOME),
Transaction(amount = 50.0, type = EXPENSE)
)
// Act
val balance = calculateBalance(account, transactions)
// Assert
assertEquals(1050.0, balance)
}
}Test Framework:
- Kotlin Test: Standard Kotlin testing framework
- JUnit: For JVM-based tests
- MockK: Mocking framework for Kotlin (previous)
- koin-test: Koin-based test utilities (post-consolidation DI replacement — see tech-debt/testing.md)
Integration Tests
Location: server/src/test/ (server-side)
Coverage Areas:
- API endpoint testing
- Database operations
- Service layer integration
- Repository layer
Test Framework:
- Ktor Test Host: For testing Ktor routes
- Testcontainers: For database testing (if used)
- Exposed Test: For database transaction testing
Example:
class FinanceRoutingTest {
@Test
fun `POST /finance/predict returns valid prediction`() = testApplication {
// Setup test data
// Make request
// Assert response
}
}End-to-End Tests
Status: Limited coverage
Coverage Areas:
- Critical user workflows
- Cross-platform behavior
- UI interactions
Challenges:
- Multi-platform testing complexity
- Compose Multiplatform testing tools
- Test data management
Testing Strategy by Module
Prediction Engine Testing
Priority: Highest
Critical Test Cases:
-
Starting Balance Calculation
- Correct calculation from initial balance + transactions
- Handles multiple accounts
- Handles empty transaction history
-
Date Point Generation
- Daily granularity generates correct dates
- Weekly granularity generates correct dates
- Monthly granularity generates correct dates
- Handles date range boundaries
-
Transaction Aggregation
- Committed transactions correctly aggregated
- Scheduled transactions correctly projected
- Periodic spends correctly applied
- Past month replication works correctly
-
Scenario Generation
- Base scenario matches expected
- Optimistic scenario applies positive noise
- Pessimistic scenario applies negative noise
- Noise percentage correctly applied
-
Edge Cases
- No transaction history
- Very long date ranges
- Negative balances
- Multiple accounts with transfers
Test Data:
- Synthetic transaction data
- Known-good prediction results
- Edge case scenarios
Finance Module Testing
Account tests (server):
AccountBalanceCalculatorTest— unit tests for signed amounts and balance formulaAccountDBTest— integration tests for CRUD, derived balance, soft-delete,getTotalBalance(Postgreslocalhost:5431/testdb)AccountRepositoryTest— verifiesupdateforwardsuserIdandaccountIdin correct order
See Accounts.
Test Areas:
- Transaction CRUD operations
- Account management
- Budget calculations
- Savings goal tracking
- Category keyword matching
Test Cases:
- Create transaction with valid data
- Reject invalid transaction amounts
- Calculate budget progress correctly
- Derived account balance reflects income/expense after transactions (balance is computed on read, not stored per transaction)
- Soft-deleted accounts excluded from list and total balance
Data Layer Testing
Test Areas:
- Repository methods
- Database queries
- Data conversions
- Transaction handling
Test Approach:
- In-memory database for fast tests
- Test transactions (rollback after each test)
- Verify data integrity
Test Organization
Naming Conventions
Tests follow a consistent naming pattern:
class FeatureServiceTest {
@Test
fun `methodName_condition_expectedResult`() {
// Test implementation
}
}Example:
@Test
fun `calculateBalance_withIncomeAndExpense_returnsCorrectBalance`() {
// Test
}Test File Structure (post-consolidation)
shared/
src/commonTest/
kotlin/com/esteban/ruano/oter/
services/
FinanceServiceTest.kt
utils/
DateUIUtilsTest.kt
server/
src/test/
kotlin/com/esteban/ruano/
service/
AccountBalanceCalculatorTest.kt
routing/
FinanceRoutingTest.kt
repository/
AccountDBTest.ktTest Data Management
Approach:
- Test fixtures: Reusable test data builders
- Factories: Object factories for creating test entities
- Fixtures: JSON files for complex test data
Example:
object TestFixtures {
fun createTransaction(
amount: Double = 100.0,
type: TransactionType = EXPENSE
): Transaction {
return Transaction(
amount = amount,
type = type,
// ... other fields with defaults
)
}
}Running Tests
Server Tests
# Run all server tests
./gradlew :server:test
# Run specific test class
./gradlew :server:test --tests TransactionServiceTest
# Run with coverage
./gradlew :server:test jacocoTestReportShared Module Tests
# Run shared module tests
./gradlew :shared:testAll Tests
# Run all tests across modules
./gradlew testCode Coverage
Current Coverage (as of 2026-06)
| Module | Test Files | Sources | Approx. Coverage |
|---|---|---|---|
server | ~53 | ~N/A | Meaningful |
shared | ~4 | ~491 | ≈ 1% |
composeApp | 0 | ~287 | 0% |
androidApp | 0 | ~2 | 0% |
See tech-debt/testing.md for the full gap analysis and rebuild plan.
Coverage Targets (aspirational)
- All modules: 70%+ (long-term)
- Prediction Engine: 90%+
- Finance Services: 80%+
Coverage Tools
- JaCoCo: Code coverage for JVM modules (server)
- Kover: Kotlin code coverage (alternative for client modules)
Coverage Reports
# Generate coverage report
./gradlew test jacocoTestReport
# View report
open build/reports/jacoco/test/html/index.htmlQuality Assurance Practices
Code Review
Process:
- All code changes require review
- At least one approval required
- Automated checks must pass
- Test coverage must not decrease
Review Checklist:
- Tests added/updated (or documented rationale for skipping)
- Documentation updated
- No breaking changes (or documented)
- Performance considered
- Security considered
Static Analysis
Tools:
- Detekt: Kotlin code analysis
- ktlint: Code style checking
- Android Lint: Android-specific checks
Configuration:
# Run static analysis
./gradlew detekt
./gradlew ktlintCheckContinuous Integration
CI Pipeline:
- Lint: Code style and static analysis
- Build: Compile all modules
- Test: Run all tests
- Coverage: Generate coverage reports
- Deploy: Deploy to staging (on main branch)
Manual Testing
Test Scenarios:
- User registration and login
- Create and edit transactions
- Generate balance predictions
- Import transactions
- Manage accounts and budgets
Test Devices:
- Desktop (Windows, macOS, Linux)
- Android (various versions)
- iOS (various versions)
- Web (Chrome, Firefox, Safari)
Testing Best Practices
Test Isolation
- Each test is independent
- No shared state between tests
- Database transactions rolled back
- Mock external dependencies
Test Data
- Use realistic but synthetic data
- Avoid hardcoded credentials
- Clean up test data after tests
- Use factories for test data creation
Assertions
- Clear assertion messages
- Test one thing per test
- Use descriptive test names
- Verify both positive and negative cases
Performance Testing
Areas:
- Prediction generation performance
- Database query performance
- API response times
- Large dataset handling
Tools:
- JMeter for API load testing
- Custom benchmarks for algorithms
- Profiling tools (JProfiler, YourKit)
Future Testing Improvements
Immediate Priority (2026)
- Rebuild client test infrastructure: Establish
koin-testpatterns, shared test fixtures, and DI test modules forsharedandcomposeApp - Migrate server tests: The server’s existing test suite (~53 files) should be adapted to any new DI conventions introduced by the consolidation
- Cover critical paths first: Balance prediction engine, finance calculations, auth flows
- Add CI gate: Block merges on test failures once baseline coverage exists
Test Coverage Goals
- Unit Tests: 80%+ coverage (long-term)
- Integration Tests: Critical paths covered
- E2E Tests: Major user workflows
Testing Tools
- Compose Testing: UI testing for Compose Multiplatform
- Screenshot Testing: Visual regression testing
- Property-Based Testing: Hypothesis-style testing for calculations
- koin-test: DI-aware testing replacing the removed Hilt
test-core
Test Automation
- Automated E2E Tests: CI/CD pipeline integration
- Performance Benchmarks: Automated performance regression detection
- Security Testing: Automated vulnerability scanning
Test Infrastructure
- Test Containers: Real database testing (server)
- Mock Server: External API mocking (WireMock / MockServer)
- Test Data Management: Centralized test data / factories
Bug Reporting
Bug Report Template
**Description:**
Clear description of the issue
**Steps to Reproduce:**
1. Step one
2. Step two
3. ...
**Expected Behavior:**
What should happen
**Actual Behavior:**
What actually happens
**Environment:**
- Platform: Desktop/Android/iOS/Web
- Version: X.Y.Z
- OS: ...
**Screenshots:**
If applicable
**Logs:**
Relevant error logsBug Triage
Priority Levels:
- P0: Critical (data loss, security)
- P1: High (broken feature)
- P2: Medium (minor issue)
- P3: Low (cosmetic)
Labels:
bug: Confirmed bugenhancement: Feature requestquestion: Question/help neededplatform-specific: Issue on specific platform