Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,10 @@ import com.juandgaines.testground.domain.Coordinates
import com.juandgaines.testground.domain.Place
import com.juandgaines.testground.domain.Profile
import com.juandgaines.testground.domain.User
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test

@OptIn(ExperimentalCoroutinesApi::class)
class NavigationTest2 {

@get:Rule
Expand Down Expand Up @@ -80,21 +76,13 @@ class NavigationTest2 {
}
// Act - Navigate to detail screen
composeRule.onNodeWithContentDescription("Place card: Test Place").performClick()
// Assert - Verify navigation state
println("Current destination: ${navController.currentDestination?.route}")
println("Current destination: ${navController.currentDestination?.navigatorName}")
println("Current destination: ${navController.currentDestination?.route}")
println("Previous back stack entry: ${navController.previousBackStackEntry?.destination?.route}")
println("Back stack size: ${navController.backStack.size}")

Truth.assertThat(navController.previousBackStackEntry?.destination?.route).isEqualTo("profile")
Truth.assertThat(navController.backStack.size).isEqualTo(2)

// Act - Navigate back
composeRule.onNodeWithContentDescription("Navigate back").performClick()
// Assert - Verify back navigation state
Truth.assertThat(navController.currentDestination?.route).isEqualTo("profile")
Truth.assertThat(navController.backStack.size).isEqualTo(1)
}

@Test
Expand Down Expand Up @@ -133,12 +121,11 @@ class NavigationTest2 {
// Act - Navigate to detail screen
composeRule.onNodeWithContentDescription("Place card: Test Place").performClick()
composeRule.waitForIdle()
// Act - Pop back stack programmatically
navController.popBackStack()

composeRule.onNodeWithContentDescription("Navigate back").performClick()
composeRule.waitForIdle()
// Assert - Verify back stack state
Truth.assertThat(navController.currentDestination?.route).isEqualTo("profile")
Truth.assertThat(navController.backStack.size).isEqualTo(1)
Truth.assertThat(navController.currentDestination?.route).contains("profile")
Truth.assertThat(navController.previousBackStackEntry).isNull()

}
Expand Down Expand Up @@ -181,7 +168,6 @@ class NavigationTest2 {

// Assert - Verify navigation arguments
val currentDestination = navController.currentDestination
Truth.assertThat(currentDestination?.route).isEqualTo("detail/1")
Truth.assertThat(currentDestination?.arguments?.get("placeId")).isEqualTo("1")
Truth.assertThat(currentDestination?.route).contains("detail")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import com.juandgaines.testground.domain.Coordinates
import com.juandgaines.testground.domain.Place
import com.juandgaines.testground.domain.Profile
import com.juandgaines.testground.domain.User
import io.mockk.mockk
import io.mockk.spyk
import io.mockk.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
import org.junit.Rule
Expand Down Expand Up @@ -72,19 +75,109 @@ class NavigationTest {

// Act - Navigate to detail
composeRule.onNodeWithContentDescription("Place card: Test Place").performClick()
composeRule.waitForIdle()

// Assert - Verify detail screen
composeRule.onNodeWithText("Place Details").assertIsDisplayed()
composeRule.onNodeWithText("Test Place").assertIsDisplayed()
composeRule.onNodeWithText("Latitude").assertIsDisplayed()
composeRule.onNodeWithText("1.0°").assertIsDisplayed()

// Act - Navigate back
composeRule.onNodeWithContentDescription("Navigate back").performClick()

composeRule.waitForIdle()
// Assert - Verify back on profile screen
composeRule.onNodeWithText("Welcome Test User!").assertIsDisplayed()
composeRule.onNodeWithContentDescription("Place card: Test Place").assertIsDisplayed()
}
}



// -------- INTERFAZ BASE PARA LOS TEST DOUBLES -------

interface ShoppingCartCache {
fun saveCart(items: List<Producto>)
fun loadCart(): List<Producto>
fun clearCart()
}

data class Producto(
val id: Int,
val nombre: String,
val precio: Double
)

// -------- 1. 🫥 DUMMY --------
class ShoppingCartCacheDummy : ShoppingCartCache {
override fun saveCart(items: List<Producto>) = Unit
override fun loadCart(): List<Producto> = emptyList()
override fun clearCart() = Unit
}

// Uso típico: se pasa como parámetro, pero no se usa. Solo cumple con la firma.

// -------- 2. 📦 STUB --------
class ShoppingCartCacheStub : ShoppingCartCache {
override fun saveCart(items: List<Producto>) = Unit
override fun loadCart(): List<Producto> = listOf(
Producto(1, "Helado", 5.0),
Producto(2, "Manzana", 2.0)
)
override fun clearCart() = Unit
}

// Uso típico: quieres que el método devuelva datos fijos y predecibles.

// -------- 3. 🧱 FAKE --------
class ShoppingCartCacheFake : ShoppingCartCache {
private val items = mutableListOf<Producto>()

override fun saveCart(items: List<Producto>) {
this.items.clear()
this.items.addAll(items)
}

override fun loadCart(): List<Producto> = items.toList()

override fun clearCart() {
items.clear()
}
}

// Uso típico: tests realistas con lógica funcional pero sin acceso a red o BD.

// -------- 4. 🪞 SPY --------
// Requiere biblioteca: mockk o Mockito
// Dependencia gradle (mockk): testImplementation "io.mockk:mockk:1.13.7"

fun ejemploSpy() {

val shoppingCartSpy = spyk(ShoppingCartCacheFake())

shoppingCartSpy.loadCart() // Acción a verificar

verify { shoppingCartSpy.loadCart() } // Verifica que se llamó
}

// -------- 5. 🎭 MOCK --------
fun ejemploMock() {

val shoppingCartMock = mockk<ShoppingCartCache>()

io.mockk.every { shoppingCartMock.loadCart() } returns listOf(
Producto(1, "Helado", 5.0)
)

val carrito = shoppingCartMock.loadCart()

verify { shoppingCartMock.loadCart() }
}

/*
* ✅ CONCLUSIÓN:
* - Fakes son los más recomendados para tests unitarios.
* - Mocks y Spies permiten verificar interacciones, pero agregan complejidad.
* - Los Dummy y Stub son útiles para cubrir casos básicos o dependencias simples.
*
* 🧠 Siempre elige el tipo de Test Double según el objetivo del test y el nivel de la pirámide.
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.juandgaines.testground.presentation

import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.juandgaines.testground.domain.Coordinates
import com.juandgaines.testground.domain.Place
import com.juandgaines.testground.domain.Profile
import com.juandgaines.testground.domain.User
import org.junit.Rule
import org.junit.Test

class NavigationTestProfile {

@get:Rule
val composeRule = createComposeRule()

@Test
fun navigation_fromProfileToDetail_worksCorrectly() {
// Arrange
val testPlace = Place(
id = "1",
name = "Test Place",
coordinates = Coordinates(1.0, 1.0)
)
val testProfile = Profile(
user = User("test-user", "Test User"),
places = listOf(testPlace)
)

// Act
composeRule.setContent {
MaterialTheme {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "profile"
) {
composable("profile") {
ProfileScreen(
state = ProfileState(profile = testProfile),
onPlaceClick = { place ->
navController.navigate("detail/${place.id}")
}
)
}
composable("detail/{placeId}") { backStackEntry ->
val placeId = backStackEntry.arguments?.getString("placeId")
val place = testProfile.places.find { it.id == placeId }
if (place != null) {
DetailPlaceScreen(
place = place,
onNavigateBack = { navController.popBackStack() }
)
}
}
}
}
}

// Assert - Verify initial screen
composeRule.onNodeWithText("Welcome Test User!").assertIsDisplayed()
composeRule.onNodeWithContentDescription("Place card: Test Place").assertIsDisplayed()

// Act - Navigate to detail
composeRule.onNodeWithContentDescription("Place card: Test Place").performClick()
composeRule.waitForIdle()

// Assert - Verify detail screen
composeRule.onNodeWithText("Place Details").assertIsDisplayed()
composeRule.onNodeWithText("Test Place").assertIsDisplayed()
composeRule.onNodeWithText("Latitude").assertIsDisplayed()

// Act - Navigate back
composeRule.onNodeWithContentDescription("Navigate back").performClick()
composeRule.waitForIdle()
// Assert - Verify back on profile screen
composeRule.onNodeWithText("Welcome Test User!").assertIsDisplayed()
composeRule.onNodeWithContentDescription("Place card: Test Place").assertIsDisplayed()
}
}