askvity

What is stub in Android?

Published in Android Testing 5 mins read

In Android development, particularly within the realm of unit testing, a stub is a simplified version of an object that is used to replace a real dependency during testing. Its primary purpose is to provide predefined responses to method calls, allowing the test to control the behavior of the dependency without relying on its actual implementation.

Stubs in Android Unit Testing

When you write unit tests for your Android code, you often encounter situations where the class you are testing depends on other classes or components, such as databases, network services, or even other parts of the Android framework. To effectively unit test a single component in isolation, you need to replace these dependencies with test doubles. Stubs are one type of test double.

Core Functionality

As highlighted by the reference, the key characteristic of a stub is that it:

"simply provide predefined responses without verifying interactions."

This means you configure the stub to return specific values when certain methods are called. The test then proceeds, and the code under test receives these predefined values from the stub instead of interacting with the real dependency.

Stubs vs. Mocks

It's common to confuse stubs and mocks, as they are both used as test doubles. However, they serve different primary purposes:

Feature Stub Mock
Primary Goal Provide predefined responses Verify interactions (how methods are called)
Focus Behavior testing (testing the logic based on returned values) Interaction testing (testing if methods were called correctly)
Verification Does not typically verify calls were made Verifies method calls, arguments, and order
Reference Quote "...stubs simply provide predefined responses without verifying interactions." "...mocks verify that specific method calls are made with expected arguments and return values..."
Use Case Controlling test flow based on dependency responses Ensuring methods are called on dependencies as expected

The reference provides a clear guideline:

"Choose mocks for interaction testing and stubs for behavior testing in your Android unit tests."

Why Use Stubs?

Using stubs offers several benefits in Android unit testing:

  • Isolation: They isolate the code being tested from its dependencies, ensuring that the test failure is due to an issue in the code under test, not in a dependency.
  • Control: They give you complete control over the responses of dependencies, allowing you to test various scenarios, including error conditions or specific data sets, that might be difficult to reproduce with the real dependency.
  • Speed: Stubs are usually lightweight and execute much faster than real dependencies (like database calls or network requests), leading to quicker test execution times.
  • Determinism: They ensure tests are deterministic and repeatable, as the dependency's response is always the same predefined value.

Practical Example

Imagine you are testing a UserRepository class that depends on a DatabaseService. In a unit test for UserRepository, you don't want to interact with an actual database. Instead, you would create a stub for DatabaseService.

// Real interface (or class)
interface DatabaseService {
    User getUserById(int userId);
}

// Class under test
class UserRepository {
    private final DatabaseService dbService;

    public UserRepository(DatabaseService dbService) {
        this.dbService = dbService;
    }

    public String getUserName(int userId) {
        User user = dbService.getUserById(userId);
        return user != null ? user.getName() : "Unknown User";
    }
}

// In your unit test
@Test
public void testGetUserName_userExists() {
    // 1. Create a stub for DatabaseService
    DatabaseService stubDbService = new DatabaseService() {
        @Override
        public User getUserById(int userId) {
            // Stubs provide predefined responses
            if (userId == 123) {
                return new User(123, "Alice");
            }
            return null; // For other IDs
        }
    };

    // 2. Create the class under test, injecting the stub
    UserRepository userRepository = new UserRepository(stubDbService);

    // 3. Test the behavior of UserRepository using the stub's response
    String userName = userRepository.getUserName(123);

    // 4. Assert the result based on the stub's predefined response
    assertEquals("Alice", userName);

    // Note: We are testing UserRepository's logic, not if getUserById was called (that would be a mock)
}

In this example, the stubDbService is a stub. It provides a predefined response (new User(123, "Alice")) when getUserById(123) is called, without verifying that the method was called. The test focuses on the UserRepository's behavior: how it handles the User object returned by its dependency.

Libraries like Mockito or Android's built-in mock support can help create stubs and mocks more easily than manual implementation. When using such libraries to create a stub, you typically use syntax like when(stubObject.someMethod()).thenReturn(someValue).

In summary, a stub in Android unit testing is a controlled replacement for a dependency that simply provides fixed outputs to enable testing the logic of the code under test based on those outputs.

Related Articles