Back to Blog

Unit Tests, Mock HTTP & UI Tests in Kotlin Compose Desktop | Lesson 15

Sandy LaneSandy Lane

Video: Unit Tests, Mock HTTP & UI Tests in Kotlin Compose Desktop | Lesson 15 by Taught by Celeste AI - AI Coding Coach

Watch full page →

Unit Tests, Mock HTTP & UI Tests in Kotlin Compose Desktop

Testing Kotlin Compose Desktop applications involves combining unit tests, HTTP mocking, and UI tests for robust coverage. This example refactors a weather app to inject an HttpClient, enabling isolated tests of business logic and UI components using Ktor's MockEngine and Compose's testing APIs.

Code

import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.*
import io.ktor.client.*
import io.ktor.client.engine.mock.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import org.junit.Rule
import org.junit.Test

// Pure function mapping WMO weather codes to descriptions
fun weatherDescription(code: Int): String = when (code) {
  0 -> "Clear"
  1 -> "Partly Cloudy"
  2 -> "Cloudy"
  else -> "Unknown"
}

// Serializable data class for weather API response
@Serializable
data class WeatherResponse(val temperature: Double, val wmoCode: Int)

// Function to fetch weather using injected HttpClient
suspend fun fetchWeather(client: HttpClient): WeatherResponse {
  val response: String = client.get("https://api.weather.com/current")
  return Json.decodeFromString(response)
}

class WeatherTests {
  @get:Rule
  val composeTestRule = createComposeRule()

  // Unit test for pure function
  @Test
  fun testWeatherDescription() {
    assert(weatherDescription(0) == "Clear")
    assert(weatherDescription(2) == "Cloudy")
    assert(weatherDescription(99) == "Unknown")
  }

  // Test JSON deserialization
  @Test
  fun testDeserialization() {
    val json = """{"temperature":22.5,"wmoCode":1}"""
    val weather = Json.decodeFromString<WeatherResponse>(json)
    assert(weather.temperature == 22.5)
    assert(weather.wmoCode == 1)
  }

  // Mock HTTP client returning a fixed JSON response
  private val mockClient = HttpClient(MockEngine) {
    engine {
      addHandler { request ->
        respond(
          content = """{"temperature":15.0,"wmoCode":0}""",
          status = HttpStatusCode.OK,
          headers = headersOf("Content-Type" to listOf(ContentType.Application.Json.toString()))
        )
      }
    }
  }

  // Test fetchWeather with mocked HTTP client
  @Test
  fun testFetchWeather() = runBlocking {
    val weather = fetchWeather(mockClient)
    assert(weather.temperature == 15.0)
    assert(weather.wmoCode == 0)
  }

  // Example Compose UI test using testTag and assertions
  @Test
  fun testWeatherScreenUI() {
    composeTestRule.setContent {
      // Compose UI showing weather description with test tag
      androidx.compose.material.Text(
        text = weatherDescription(1),
        modifier = androidx.compose.ui.Modifier.testTag("weatherText")
      )
    }

    // Assert UI text matches expected description
    composeTestRule.onNodeWithTag("weatherText")
      .assertExists()
      .assertTextEquals("Partly Cloudy")
  }
}

Key Points

  • Injecting HttpClient enables mocking HTTP calls with Ktor's MockEngine for reliable tests.
  • Pure functions like weatherDescription simplify unit testing by isolating logic from side effects.
  • Use kotlinx.serialization to test JSON deserialization of API responses directly.
  • Compose UI tests use createComposeRule, Modifier.testTag, and semantic queries for stable assertions.
  • waitUntil and assertExists/assertTextEquals help verify asynchronous UI updates and error states.