Learn Android testing fundamentals from basic to interview level: software testing, manual vs automated testing, AAA test structure, test boundaries, testing pyramid, unit/integration/UI/E2E testing, coverage, risk-based testing, regression testing, and deciding what should or should not be tested.

1. What is Software Testing?

Software testing is the process of checking whether an application:

  • Works according to requirements
  • Produces the expected result
  • Handles errors correctly
  • Does not break existing functionality
  • Provides a reliable user experience

A test normally provides an input, performs an action, and verifies the output.

Simple example

Requirement:

The login button should remain disabled when the password is empty.

Test:

@Test
fun emptyPassword_loginIsNotAllowed() {
    val result = LoginValidator.isValid(
        email = "user@example.com",
        password = ""
    )

    assertFalse(result)
}

Testing does not prove that an application has zero defects. It reduces risk and increases confidence.


2. Why Android Applications Need Testing

Android applications run in many different conditions:

  • Different Android versions
  • Different screen sizes
  • Different manufacturers
  • Different memory and processor capabilities
  • Slow or unavailable networks
  • Activity recreation
  • Configuration changes
  • Background and foreground transitions
  • Process death
  • Permission changes

Banking application use case

Suppose a user presses Pay ₹1,000, but the network times out.

Testing must verify that:

  • The payment is not submitted twice
  • The app shows a pending state
  • The app checks transaction status
  • Rotation does not lose the transaction ID
  • A delayed success response updates the screen correctly

Without tests, small code changes can produce serious financial or user-experience problems.


3. Manual Testing vs Automated Testing

Manual testing

A person opens the application and performs test steps.

Example:

1. Open the login screen.
2. Enter an invalid password.
3. Tap Login.
4. Verify the error message.

Automated testing

A test program performs actions and verifies results automatically.

@Test
fun invalidPassword_returnsError() {
    val result = validator.validatePassword("123")

    assertEquals(
        ValidationResult.Error("Password is too short"),
        result
    )
}

Comparison

PointManual testingAutomated testing
Performed byHuman testerTest code
Initial effortLowHigher
Repeated executionSlowFast
Human observationStrongLimited
Regression testingExpensiveEffective
Exploratory testingExcellentNot suitable
Complex visual issuesUsefulSometimes difficult
CI/CD executionNot possiblePossible
ConsistencyCan varyConsistent
MaintenanceTest documentsTest code

When to use manual testing

  • Exploratory testing
  • Usability evaluation
  • Visual appearance
  • New or frequently changing feature
  • One-time verification
  • Accessibility checks requiring human evaluation

When to automate

  • Business logic
  • Repeated regression scenarios
  • Validation rules
  • State transitions
  • API and database integration
  • Critical user journeys
  • Tests executed on every pull request

A good strategy uses both manual and automated testing.


4. Arrange–Act–Assert Test Structure

Arrange–Act–Assert, or AAA, divides a test into three parts.

flowchart LR
    A["Arrange<br/>Prepare data and dependencies"]
    B["Act<br/>Execute the behaviour"]
    C["Assert<br/>Verify the result"]

    A --> B --> C

Example

@Test
fun validCredentials_loginReturnsSuccess() {
    // Arrange
    val repository = FakeLoginRepository()
    val useCase = LoginUseCase(repository)

    // Act
    val result = useCase(
        email = "user@example.com",
        password = "Password@123"
    )

    // Assert
    assertEquals(LoginResult.Success, result)
}

Arrange

Prepare:

  • Input data
  • Object under test
  • Fake or mock dependencies
  • Expected result

Act

Execute the behaviour being tested.

Assert

Compare the actual result with the expected result.

Best practice

Ideally, a test should contain one meaningful action and verify one behaviour. Multiple assertions are acceptable when they verify the same behaviour.


5. Test Naming Conventions

A test name should explain:

1. What is being tested
2. Under which condition
3. What result is expected

Recommended format

methodOrFeature_condition_expectedResult

Examples:

@Test
fun login_emptyPassword_returnsValidationError()

@Test
fun loadProfile_repositoryFails_emitsErrorState()

@Test
fun transfer_amountExceedsBalance_isRejected()

Kotlin backtick style can also be used:

@Test
fun `empty password returns validation error`() {
  // Test
}

Avoid unclear names

@Test
fun testLogin()    // Too broad

@Test
fun test1()        // No meaning

@Test
fun checkData()    // Expected result is unknown

A good test name acts like documentation.


6. Test Scope and Test Boundaries

Test scope

Test scope defines how much of the system a test covers.

Examples:

  • One validation function
  • One ViewModel
  • Repository and database
  • Complete payment journey

Test boundary

A boundary defines which components are included as real components and which are replaced.

ViewModel unit-test boundary

flowchart LR
    UI["UI<br/>Outside boundary"]
    VM["Real ViewModel"]
    FR["Fake repository"]
    API["Real API<br/>Outside boundary"]

    UI -.-> VM
    VM --> FR
    FR -.-> API

In this test:

  • ViewModel is real.
  • Repository is fake.
  • UI, network and database are excluded.

Repository integration-test boundary

flowchart LR
    VM["ViewModel<br/>Excluded"]
    R["Real repository"]
    DB["Real in-memory database"]
    API["Fake HTTP server"]

    VM -.-> R
    R --> DB
    R --> API

Clear boundaries help answer:

  • What failure does this test locate?
  • Which dependencies should be real?
  • Which dependencies should be controlled?
  • How fast should the test run?


7. Behaviour Testing vs Implementation Testing

Behaviour testing

Checks the result visible through the public API.

@Test
fun validTask_saveTask_returnsSuccess() {
    val result = useCase.save("Learn testing")

    assertEquals(SaveResult.Success, result)
}

It does not care how the result was internally produced.

Implementation testing

Checks internal details such as:

  • Private methods
  • Exact internal call sequence
  • Internal variables
  • Number of calls that are not part of a requirement

verify(exactly = 1) {
    repository.validate()
}

This verification is useful only if calling validate() exactly once is an actual requirement.

Comparison

Behaviour testingImplementation testing
Tests observable resultsTests internal details
Survives refactoringOften breaks during refactoring
Describes requirementsDescribes current implementation
Usually preferredUse only when interaction matters

Important rule

Test:

What must the component do?

Avoid testing:

How has the developer currently written it?


8. Testing Strategy

A testing strategy is a planned approach that explains:

  • What will be tested
  • What will not be tested
  • Which test layer will be used
  • Which tools will be used
  • When tests will run
  • Who owns failures
  • What quality level is required

Example Android testing strategy

ComponentTest typeTool
ValidationUnitJUnit
Use caseUnitJUnit + fake
ViewModel stateUnitJUnit + coroutine test
Repository mappingIntegrationJUnit
Room queriesIntegrationRoom in-memory database
Retrofit requestsIntegrationMockWebServer
Compose screenUICompose Test
XML screenUIEspresso
Critical payment journeyE2EInstrumented test

Strategy creation steps

1. Identify important features.
2. Assess business and technical risks.
3. Define test boundaries.
4. Select the cheapest reliable test layer.
5. Automate repeatable scenarios.
6. Keep a small number of critical E2E tests.
7. Execute tests in CI.
8. Monitor failures and flaky tests.
9. Update the strategy as the application changes.


9. Testing Pyramid

The testing pyramid recommends:

  • Many unit tests
  • A reasonable number of integration tests
  • Fewer UI tests
  • Very few end-to-end tests

flowchart BT
    U["Unit tests<br/>Many • Fast • Low cost"]
    I["Integration tests<br/>Some • Medium speed"]
    UI["UI tests<br/>Few • Slower"]
    E["End-to-end tests<br/>Very few • Highest cost"]

    U --> I --> UI --> E

It is a guideline, not a fixed percentage.

Example distribution:

  • Unit tests: 60–70% 
  • Integration tests: 20–30% 
  • UI/E2E tests: 10–15%

The correct distribution depends on:

  • Application architecture
  • Business risk
  • Team size
  • Legacy code
  • Frameworks
  • Device-dependent behaviour


10. Unit-Test Layer

A unit test verifies a small unit in isolation.

Possible units:

  • Function
  • Validator
  • Mapper
  • Use case
  • ViewModel
  • Reducer
  • State holder

Use case

Test whether a transfer is rejected when its amount exceeds the available balance.

class TransferValidator {

    fun canTransfer(
        amount: Double,
        balance: Double
    ): Boolean {
        return amount > 0 && amount <= balance
    }
}
@Test
fun amountExceedsBalance_transferIsRejected() {
    val validator = TransferValidator()

    val result = validator.canTransfer(
        amount = 6_000.0,
        balance = 5_000.0
    )

    assertFalse(result)
}

Characteristics

  • Fast
  • Isolated
  • Deterministic
  • Easy to debug
  • Inexpensive to maintain

Limitation

A unit test cannot prove that different components work correctly together.


11. Integration-Test Layer

An integration test verifies that multiple components communicate correctly.

Examples:

  • Repository + DAO
  • Repository + API
  • Retrofit + JSON converter
  • Room + database migration
  • Use case + repository

Use case

Verify that API data is mapped and saved in the database:

Fake API response 
↓ 
Real repository 
↓ 
Real mapper 
 ↓    
In-memory Room database
 ↓ 
Verify stored entity

Important integration scenarios

  • Correct API endpoint
  • JSON parsing
  • DTO-to-domain mapping
  • Database queries
  • Cache behaviour
  • Error mapping
  • Offline behaviour
  • Migration correctness

Integration tests are slower than unit tests but provide confidence at component boundaries.


12. UI-Test Layer

A UI test interacts with a screen and verifies visible behaviour.

It can test:

  • Text fields
  • Buttons
  • Error messages
  • Lists
  • Dialogs
  • Navigation
  • Loading indicators
  • Accessibility semantics

Compose example

@Test
fun emptyTitle_saveClicked_displaysError() {
    composeRule
        .onNodeWithText("Save")
        .performClick()

    composeRule
        .onNodeWithText("Title is required")
        .assertIsDisplayed()
}

Espresso example

@Test
fun emptyEmail_loginClicked_displaysError() {
    onView(withId(R.id.loginButton))
        .perform(click())

    onView(withText("Email is required"))
        .check(matches(isDisplayed()))
}

Limitation

UI tests are generally:

  • Slower
  • More environment-dependent
  • More expensive to maintain
  • Harder to diagnose

Use them for important screen behaviour, not every business rule.


13. End-to-End Test Layer

An E2E test verifies a complete user journey through multiple layers.

Login journey

flowchart TD
    L["Open login screen"]
    C["Enter credentials"]
    A["Call authentication API"]
    S["Store authenticated session"]
    H["Display home screen"]

    L --> C --> A --> S --> H

Possible E2E scenarios

  • Registration → login → home
  • Search product → add to cart → checkout
  • Add beneficiary → transfer money → confirmation
  • Create task → restart app → task remains available

Advantages

  • High confidence in a complete journey
  • Detects integration and configuration failures
  • Represents real user behaviour

Disadvantages

  • Slow
  • Expensive
  • Difficult to debug
  • More likely to become flaky
  • Requires complex test data and environment setup

Automate only the most critical journeys.


14. Comparing Testing Layers

FactorUnitIntegrationUIE2E
ScopeSingle unitMultiple componentsScreen behaviourComplete journey
SpeedVery fastFast/mediumSlowSlowest
IsolationHighMediumLowVery low
ConfidenceLogic confidenceBoundary confidenceUI confidenceJourney confidence
Failure diagnosisEasyModerateHarderHardest
Maintenance costLowMediumHighVery high
Typical quantityManySomeFewVery few
Android device neededUsually noSometimesUsually yesUsually yes
ExamplesValidatorRepository + RoomLogin screenLogin → Home


15. Test Confidence

Test confidence means how strongly the tests indicate that the application works correctly.

Confidence depends on:

  • Test relevance
  • Quality of assertions
  • Realistic test data
  • Correct boundaries
  • Coverage of important scenarios
  • Test reliability
  • Production-like integration

Important observation

A single E2E test offers broad confidence but poor failure localization.

Many unit tests offer excellent logic confidence but cannot prove that the complete application works.

Therefore, confidence comes from a balanced test suite.


16. Test Execution Speed

Execution speed affects how frequently developers run tests.

TestApproximate relative speed
Pure unit testFastest
JVM integration testFast
Robolectric testMedium
Instrumented integration testSlower
UI testSlow
Complete E2E suiteSlowest

These are relative classifications, not guaranteed timings.

Why speed matters

Fast tests:

  • Run on every change
  • Provide early feedback
  • Reduce debugging time
  • Encourage developers to test locally
  • Keep CI pipelines efficient
  • Place most business logic in fast JVM-testable components.


17. Test Maintenance Cost

Maintenance cost includes the effort required to:

  • Write tests
  • Understand failures
  • Update tests after changes
  • Manage test data
  • Maintain devices and emulators
  • Repair flaky tests
  • Maintain backend test environments

Cost generally increases upward

Unit < Integration < UI < End-to-end

Example

Changing a private ViewModel implementation should not break a behaviour-based unit test.

Changing a screen layout may break several UI selectors even when the feature still works.

Good test architecture reduces maintenance by using:

  • Stable public APIs
  • Stable semantics or test tags
  • Fake dependencies
  • Test fixtures
  • Clear test boundaries
  • Behaviour-focused assertions


18. Test Coverage

Test coverage measures which code was executed during tests.

Common forms:

  • Line coverage
  • Branch coverage
  • Function coverage
  • Instruction coverage

Example

fun discount(isPremium: Boolean): Int {
  return if (isPremium) 20 else 5
}

Testing only isPremium = true might execute most lines but fail to test the non-premium branch.

Coverage limitations

High coverage does not guarantee good tests.

A test can execute code without verifying the correct result:

@Test
fun weakTest() {
  calculator.add(2, 3) // No assertion
}

Coverage should be used to find missing areas—not as the only quality measurement.

Focus particularly on:

  • Business-critical branches
  • Error paths
  • State transitions
  • Complex logic
  • Security-sensitive logic


19. Risk-Based Testing

Risk-based testing gives more testing effort to features with greater risk.

A simple model is:

[
\text{Risk} = \text{Probability of failure} \times \text{Impact of failure}
]

Example risk table

FeatureFailure probabilityImpactPriority
Payment submissionMediumCriticalVery high
Login authenticationMediumHighHigh
Profile imageMediumLowMedium
Button colourLowLowLow

High-risk Android areas

  • Authentication
  • Payments
  • Personal information
  • Database migrations
  • Offline synchronization
  • Security
  • Deep links
  • Permissions
  • Process-death restoration
  • Release configuration

Payment example

For payment submission, test:

  • Successful payment
  • Insufficient balance
  • Network timeout
  • Duplicate button tap
  • Duplicate API request
  • Pending transaction
  • Server failure
  • Process death
  • Retry and status check


20. Deciding What Should Be Tested

Ask the following questions:

1. Is the behaviour business-critical?
2. Is the logic complex?
3. Has it failed before?
4. Does it change frequently?
5. Does it connect two components?
6. Can failure cause data or money loss?
7. Is the error difficult to detect manually?
8. Does it behave differently across Android states?
9. Will the scenario be tested repeatedly?
10. Is automation cheaper over time?

Strong candidates

  • Validation rules
  • Calculations
  • Use cases
  • ViewModel state transitions
  • Error mapping
  • Repository caching
  • API parsing
  • Database queries and migrations
  • Authentication
  • Payment flows
  • Critical navigation
  • Offline and recovery behaviour

Selecting the layer

Use the lowest-cost layer that provides sufficient confidence.

Example:

RequirementAppropriate layer
Reject negative amountUnit
API result is cachedIntegration
Error appears on screenUI
Complete payment succeedsE2E


21. Deciding What Should Not Be Tested

Generally avoid testing:

  • Kotlin or Java language behaviour
  • Android framework implementation
  • Third-party library internals
  • Simple data-class getters
  • Private functions directly
  • Trivial constant assignments
  • Internal implementation with no behavioural value
  • Every possible UI combination
  • Exact animations unless they are requirements
  • Generated code
  • Logging statements

Example

Do not test that Kotlin stores a constructor property:

data class User(val name: String)

A test like this provides little value:

@Test
fun userStoresName() {
  assertEquals("Mahesh", User("Mahesh").name)
}

However, test custom behaviour implemented around that model.

Exception

Even simple code may need tests when it is:

  • Safety-critical
  • Financially critical
  • Security-sensitive
  • Required by regulation
  • Historically defective


22. Happy-Path Testing

A happy-path test verifies the expected successful scenario.

Login use case

Given:

  • Email is valid
  • Password is correct
  • Network is available
  • Server responds successfully

Expected:

  • Login succeeds
  • Session is stored
  • Home screen opens

@Test
fun validCredentials_repositorySucceeds_returnsSuccess() {
    repository.loginResult = LoginResult.Success

    val result = loginUseCase(
        "user@example.com",
        "Password@123"
    )

    assertEquals(LoginResult.Success, result)
}

Happy-path testing is necessary, but it is not enough.


23. Negative-Path Testing

Negative-path testing checks invalid input and failure conditions.

Examples

  • Incorrect password
  • Missing email
  • API returns 401
  • Server returns 500
  • Database write fails
  • User denies permission
  • Network disconnects
  • Invalid deep link
  • Payment is rejected

@Test
fun repositoryThrowsException_returnsNetworkError() {
    repository.shouldFail = true

    val result = loginUseCase(
        "user@example.com",
        "Password@123"
    )

    assertEquals(LoginResult.NetworkError, result)
}

Good negative-path tests verify both:

  • Correct error result
  • No incorrect side effect

For example, a failed payment must not be stored as successful.


24. Edge and Boundary-Value Testing

Boundary testing checks values around important limits.

Suppose the allowed transfer range is ₹1–₹1,00,000.

Test:

AmountExpected
₹0Reject
₹1Accept
₹2Accept
₹99,999Accept
₹1,00,000Accept
₹1,00,001Reject
@Test
fun maximumAllowedAmount_isAccepted() {
    assertTrue(validator.isValidAmount(100_000))
}

@Test
fun amountAboveMaximum_isRejected() {
    assertFalse(validator.isValidAmount(100_001))
}

Other boundaries:

  • Empty string
  • One character
  • Maximum allowed length
  • Maximum length plus one
  • Zero items
  • One item
  • First and last list position
  • Date before/on/after expiry
  • API page size
  • File-size limit
  • Integer overflow
  • Null and missing values

Defects commonly appear at boundaries rather than typical values.


25. Regression Testing

Regression testing checks whether existing functionality still works after a change.

Example

A developer changes login code to support biometric authentication. Regression tests should verify that:

  • Password login still works
  • Incorrect password still fails
  • Session storage still works
  • Logout clears the session
  • Token-expiry handling still works

Regression flow

flowchart LR
    C["Code change"]
    U["Run unit tests"]
    I["Run integration tests"]
    UI["Run selected UI tests"]
    R["Release confidence"]

    C --> U --> I --> UI --> R

Types

Full regression

Run the entire relevant test suite.

Use before:

  • Major production release
  • Significant architecture change
  • Large dependency upgrade

Selective regression

Run tests affected by the change.

Use during:

  • Pull requests
  • Small bug fixes
  • Regular development

Bug regression test

When a bug is found:

1. Write a test that reproduces it.
2. Confirm that the test fails.
3. Fix the bug.
4. Confirm that the test passes.
5. Keep the test to prevent recurrence.


Complete Use Case: Testing a Money Transfer Feature

Requirement

A user can transfer between ₹1 and ₹1,00,000 when sufficient balance is available.

Testing plan

ScenarioTest layerTest type
Validate minimum amountUnitBoundary
Reject amount above balanceUnitNegative
Successful transfer resultUnitHappy path
Repository maps API responseIntegrationContract
Timeout becomes pending stateIntegrationNegative
Error message appearsUIBehaviour
Double tap creates one requestIntegration/UIRisk-based
Beneficiary → payment → confirmationE2EHappy path
Previous transfer flow after refactoringMultipleRegression

Feature test pyramid

flowchart BT
    U["Unit<br/>Amount, balance and state rules"]
    I["Integration<br/>Repository, API and database"]
    UI["UI<br/>Input, message and navigation"]
    E["E2E<br/>Complete transfer journey"]

    U --> I --> UI --> E


Common Mistakes

1. Writing only happy-path tests
2. Testing private methods directly
3. Depending heavily on mocks
4. Writing too many E2E tests
5. Ignoring integration boundaries
6. Using coverage as the only quality goal
7. Giving tests unclear names
8. Mixing multiple unrelated behaviours in one test
9. Not controlling network, clock or random data
10. Using fixed delays such as Thread.sleep()
11. Writing tests after bugs without keeping regression tests
12. Expecting unit tests to prove the complete app works
13. Testing framework code instead of application behaviour
14. Ignoring boundary values
15. Allowing tests to depend on execution order


Interview Questions and Answers

1. What is software testing?

Software testing verifies that an application meets its requirements, handles failures correctly and does not break existing behaviour.

2. Why is testing important in Android?

Android apps run across different devices, API levels, lifecycle states and network conditions. Testing reduces compatibility issues, crashes and regressions.

3. Does testing prove that an application is bug-free?

No. Testing increases confidence and reduces risk, but it cannot prove that every possible defect has been removed.

4. What is the difference between manual and automated testing?

Manual testing is performed by a person. Automated testing uses code to execute actions and verify results repeatedly.

5. Can automated testing completely replace manual testing?

No. Automation is effective for repeatable checks, while manual testing remains valuable for exploration, usability and visual evaluation.

6. What is Arrange–Act–Assert?

* Arrange: prepare input and dependencies.

* Act: execute the behaviour.

* Assert: verify the result.

7. How should a test be named?

A useful format is:

method_condition_expectedResult

Example:

login_emptyPassword_returnsError

8. What is test scope?

Test scope defines how much of the application is covered by a test, such as one function, one screen or a complete user journey.

9. What is a test boundary?

A test boundary defines which components are real and which are excluded or replaced with test doubles.

10. What is behaviour testing?

Behaviour testing checks observable inputs and outputs through public APIs without depending on internal implementation.

11. Why should implementation-detail testing be avoided?

It creates fragile tests that break during refactoring even when application behaviour remains correct.

12. What is a testing strategy?

It is a plan defining what to test, which layers and tools to use, when tests run and what quality criteria must be met.

13. What is the testing pyramid?

It recommends many fast unit tests, some integration tests, fewer UI tests and very few E2E tests.

14. Is the testing pyramid percentage fixed?

No. The distribution depends on the application’s architecture, complexity, risks and team requirements.

15. What is a unit test?

A unit test verifies a small component, such as a function, use case or ViewModel, in isolation.

16. What is an integration test?

An integration test verifies that multiple components, such as a repository and database, work correctly together.

17. What is a UI test?

A UI test interacts with screen elements and verifies visible behaviour such as messages, lists and navigation.

18. What is an end-to-end test?

An E2E test verifies a complete user journey across UI, business logic, data and external integrations.

19. Unit test vs integration test?

A unit test isolates one component using controlled dependencies. An integration test uses multiple collaborating components.

20. UI test vs E2E test?

A UI test can verify one screen with fake dependencies. An E2E test verifies a complete journey through multiple real layers.

21. Which test layer is fastest?

Pure local unit tests are normally the fastest.

22. Which test layer provides the broadest confidence?

E2E tests provide broad journey confidence, but they are slower, costlier and harder to debug.

23. Why not test everything through the UI?

UI tests are slow, fragile and expensive. Most business rules can be verified more efficiently with unit tests.

24. What is test confidence?

It is the level of trust that the test suite provides about application correctness.

25. How can test execution speed be improved?

Use more JVM tests, inject dependencies, avoid real networks and delays, parallelize suitable tests and keep E2E tests limited.

26. What is test maintenance cost?

It is the effort needed to write, update, run and repair tests and their supporting environments.

27. What is test coverage?

Coverage measures which lines, branches, instructions or functions execute during tests.

28. Does 100% coverage mean the application is correct?

No. Code may execute without meaningful assertions, and missing requirements cannot be detected by coverage alone.

29. What is branch coverage?

Branch coverage measures whether different decision outcomes, such as both if and else, have been executed.

30. What is risk-based testing?

It prioritizes testing based on the probability and impact of failure.

31. Which features should receive the most testing?

Business-critical, complex, frequently changed, security-sensitive and historically defective features.

32. What should generally not be tested?

Language behaviour, simple getters, third-party internals, framework implementation and private methods directly.

33. What is happy-path testing?

It verifies the normal successful scenario with valid input and expected conditions.

34. What is negative-path testing?

It verifies invalid inputs, errors, exceptions and unavailable dependencies.

35. What is boundary-value testing?

It verifies values at, immediately below and immediately above defined limits.

36. Why are boundary tests important?

Many programming errors occur at limits due to incorrect comparison operators, indexing or missing validation.

37. What is regression testing?

Regression testing verifies that existing functionality still works after changes.

38. What is a regression test for a bug?

It is a permanent automated test that first reproduces a defect and later prevents the same defect from returning.

39. What is selective regression testing?

It runs the tests most relevant to the changed components instead of executing the entire suite.

40. How do you decide the correct test layer?

Use the lowest-cost and fastest layer that provides sufficient confidence for the requirement.

41. What is the ideal Android test strategy?

Use fast unit tests for logic, integration tests for boundaries, UI tests for important screen behaviour and a small number of E2E tests for critical journeys.


Quick Revision

Unit test → Is one component correct? 
Integration → Do components work together?
 UI test → Does the screen behave correctly? 
End-to-end → Does the complete user journey work?
Happy path → Expected success 
Negative path → Failure or invalid condition 
Boundary test → Values around limits 
Regression test → Existing feature after a change
Coverage → Code executed 
Confidence → Trust provided 
Strategy → Overall testing plan 
Boundary → What is included and replaced