NASA Explorer uses a combination of unit tests and instrumented tests to ensure code quality and reliability. The project follows Android testing best practices with JUnit and Espresso.
When writing unit tests for NASA Explorer, focus on:
1
ViewModel Logic
Test state management and business logic in ViewModels:
@Testfun `getDailyImage updates state correctly`() = runTest { // Given val repository = mockk<NasaRepository>() val viewModel = DailyImageViewModel(repository) // When coEvery { repository.getDailyImage() } returns Result.success(mockNasaModel) viewModel.loadDailyImage() // Then assertTrue(viewModel.uiState.value is UiState.Success)}
2
Repository Functions
Test data transformation and API response handling:
@Testfun `repository maps API response to domain model`() = runTest { // Given val apiService = mockk<NasaApiService>() val repository = NasaRepository(apiService) // When coEvery { apiService.getApod(any(), any()) } returns mockApiResponse val result = repository.getDailyImage() // Then assertTrue(result.isSuccess) assertEquals("Expected Title", result.getOrNull()?.title)}
3
Domain Model Validation
Test data model transformations and validation logic:
@Testfun `NasaModel correctly parses date format`() { val model = NasaModel( title = "Test", date = "2024-03-06", // ... other fields ) assertTrue(model.isValidDate())}
package com.ccandeladev.nasaexplorerimport androidx.test.platform.app.InstrumentationRegistryimport androidx.test.ext.junit.runners.AndroidJUnit4import org.junit.Testimport org.junit.runner.RunWithimport org.junit.Assert.*@RunWith(AndroidJUnit4::class)class ExampleInstrumentedTest { @Test fun useAppContext() { // Context of the app under test val appContext = InstrumentationRegistry.getInstrumentation().targetContext assertEquals("com.ccandeladev.nasaexplorer", appContext.packageName) }}
@Testfun `repository handles API errors gracefully`() = runTest { // Given val apiService = mockk<NasaApiService>() val repository = NasaRepository(apiService) // When coEvery { apiService.getApod(any(), any()) } throws IOException("Network error") val result = repository.getDailyImage() // Then assertTrue(result.isFailure)}
@Testfun `viewModel loads data asynchronously`() = runTest { val viewModel = MyViewModel(repository) viewModel.loadData() advanceUntilIdle() // Wait for all coroutines to complete assertTrue(viewModel.uiState.value is UiState.Success)}
name: Run Testson: [push, pull_request]jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' - name: Run Unit Tests run: ./gradlew test - name: Run Instrumented Tests uses: reactivecircus/android-emulator-runner@v2 with: api-level: 29 script: ./gradlew connectedCheck
Instrumented tests in CI require an Android emulator, which can significantly increase build times. Consider running them only on pull requests or main branch commits.