androidengineers.Book a session

Testing Architecture

Test Strategy & Pyramid

article20 minMedium

The test pyramid is a model for how to distribute tests across three levels: unit, integration, and end-to-end (E2E). Each level has a different speed/coverage trade-off. Getting the ratio right keeps your test suite fast and reliable.

The Three Levels

        /\
       /E2E\         ← Few: slow, fragile, but test real user flows
      /------\
     /Integr- \      ← Some: test component interactions
    /  ation   \
   /------------\
  /  Unit Tests  \   ← Many: fast, isolated, cheap to maintain
 /________________\

Unit Tests (Base of the Pyramid)

Test a single function, class, or module in isolation. No Android runtime needed — runs on JVM.

class PriceFormatterTest {
    private val formatter = PriceFormatter(currency = "USD")

    @Test
    fun `formats integer price with dollar sign`() {
        assertEquals("$5.00", formatter.format(500))  // cents
    }

    @Test
    fun `formats zero as free`() {
        assertEquals("Free", formatter.format(0))
    }

    @Test
    fun `formats negative price throws`() {
        assertThrows<IllegalArgumentException> { formatter.format(-1) }
    }
}

Target: 70% of your tests. They run in milliseconds, give precise feedback, and are easy to fix.

Integration Tests (Middle of the Pyramid)

Test how components work together. May require a real database, repository + DAO interaction, or multiple classes working together.

@RunWith(AndroidJUnit4::class)
class ArticleRepositoryTest {
    @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule()

    private lateinit var db: AppDatabase
    private lateinit var repository: ArticleRepository

    @Before
    fun setup() {
        db = Room.inMemoryDatabaseBuilder(
            ApplicationProvider.getApplicationContext(),
            AppDatabase::class.java
        ).build()
        repository = ArticleRepository(db.articleDao(), FakeApi())
    }

    @After
    fun teardown() = db.close()

    @Test
    fun `insert and retrieve article`() = runTest {
        repository.saveArticle(testArticle)
        val result = repository.getArticle(testArticle.id)
        assertEquals(testArticle, result)
    }
}

Target: 20% of your tests. Slower than unit tests but catch integration bugs unit tests miss.

E2E / UI Tests (Tip of the Pyramid)

Test full user flows through the real UI on a device or emulator.

@RunWith(AndroidJUnit4::class)
class LoginFlowTest {
    @get:Rule val composeTestRule = createAndroidComposeRule<MainActivity>()

    @Test
    fun `successful login navigates to home`() {
        composeTestRule.onNodeWithTag("email_field")
            .performTextInput("user@example.com")
        composeTestRule.onNodeWithTag("password_field")
            .performTextInput("password123")
        composeTestRule.onNodeWithTag("login_button")
            .performClick()

        composeTestRule.onNodeWithText("Welcome back!").assertIsDisplayed()
    }
}

Target: 10% of your tests. Run before release, not on every commit. Slow and flaky — keep them minimal.

What to Test at Each Level

ConcernUnitIntegrationE2E
Business logic✅ Primary
Data transformation
DAO + Room
Repository (API + Cache)
ViewModel logic✅ (with fakes)
Navigation
Full user flows
Critical paths (login, checkout)

The Anti-Pyramid: What to Avoid

An "ice cream cone" (most tests are E2E, few unit tests) is a common mistake:

  • E2E tests are slow → CI takes hours
  • E2E tests are flaky → random failures erode confidence
  • When a test fails, it's hard to diagnose which layer broke

Key Takeaways

LevelCountSpeedFeedback
Unit~70%< 1s eachPrecise — pinpoints the broken function
Integration~20%1–10s eachModerate — catches component interaction bugs
E2E~10%30s–5min eachCoarse — confirms full flows work

The pyramid ratio exists because unit tests give you the most value per minute of CI time. Start there — every business logic function should have unit tests before you write integration or E2E tests.

YOUR LEARNING JOURNEY

0 of 177 available lessons completed

Progress saved in this browser. No account needed.
Test Strategy & Pyramid | Android System Design | Android Engineers