Comprehensive testing approach for quality assurance.
┌─────────────┐
│ E2E Tests │ (Few, slow, high confidence)
└─────────────┘
┌───────────────┐
│ Integration │ (Some, medium speed)
│ Tests │
└───────────────┘
┌──────────────────────┐
│ Unit Tests │ (Many, fast, focused)
└──────────────────────┘
Location: app/src/test/java/
Example:
class PaymentCalculatorTest {
@Test
fun `calculates correct payment date`() {
val card = CreditCard(
statementGenerationDay = 18,
institution = BankInstitution.CHASE,
currentBalance = 1500.0
)
val recommendation = PaymentCalculator.calculateRecommendation(card)
assertEquals(LocalDate.of(2025, 10, 15), recommendation.recommendedPaymentDate)
assertEquals(1500.0, recommendation.recommendedAmount)
}
}Run:
./gradlew test
./gradlew test --tests PaymentCalculatorTestLocation: app/src/androidTest/java/
Example:
@RunWith(AndroidJUnit4::class)
class MainActivityTest {
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun testMainScreen() {
composeTestRule.setContent {
CreditCardOptimizerTheme {
MainScreen(viewModel)
}
}
composeTestRule.onNodeWithText("Connect with Plaid").assertExists()
}
}Run:
./gradlew connectedAndroidTestLocation: backend/src/__tests__/
Example:
describe('AuthService', () => {
it('should register user', async () => {
const result = await authService.register(
'test@example.com',
'TestPass123'
);
expect(result.user.email).toBe('test@example.com');
expect(result.token).toBeDefined();
});
it('should reject weak password', async () => {
await expect(
authService.register('test@example.com', 'weak')
).rejects.toThrow();
});
});Run:
npm test
npm test -- --coverageMinimum: 95%
npm run coverage
# Should show:
# Statements: 95%+
# Branches: 95%+
# Functions: 95%+
# Lines: 95%+Enforce in CI:
npm run coverage -- --coverageThreshold='{"global":{"lines":95}}'Test credentials:
- Username:
user_good - Password:
pass_good - MFA code:
1234
Test accounts:
- Chase: Credit card with $10,000 limit
- Bank of America: Credit card with $15,000 limit
object TestData {
val mockCreditCard = CreditCard(
id = "test-123",
name = "Test Card",
statementGenerationDay = 18,
currentBalance = 1500.0
)
val mockRecommendation = PaymentRecommendation(
creditCard = mockCreditCard,
recommendedPaymentDate = LocalDate.now().plusDays(5)
)
}@Test
fun testRepository() {
val mockApi = mock<PlaidApi>()
val repository = PlaidRepository(mockApi)
whenever(mockApi.getAccounts()).thenReturn(listOf(mockAccount))
val result = runBlocking { repository.getAccounts() }
assertEquals(1, result.getOrNull()?.size)
}jest.mock('../repositories/userRepository');
test('should call repository', async () => {
userRepository.findByEmail.mockResolvedValue(mockUser);
const user = await authService.login('test@example.com', 'password');
expect(userRepository.findByEmail).toHaveBeenCalled();
});-
Test Naming: Use descriptive names
fun `should reject payment for card without statement date`()
-
Arrange-Act-Assert:
// Arrange val card = createTestCard() // Act val result = calculate(card) // Assert assertEquals(expected, result)
-
Test One Thing: Each test should test one behavior
-
Mock External Dependencies: Don't rely on actual APIs
-
Clean Up: Reset state after each test
# GitHub Actions example
- name: Run Android Tests
run: ./gradlew test
- name: Run Backend Tests
run: cd backend && npm test
- name: Check Coverage
run: cd backend && npm run coverageSee Also: