Back to Repositories

Testing LottieTask Listener Management Implementation in Lottie Android

This test suite validates the functionality of LottieTask class in the Lottie Android animation library, focusing on listener management and error handling. The tests ensure proper callback execution and exception handling for animation task processing.

Test Coverage Overview

The test suite provides comprehensive coverage of LottieTask functionality, including success and failure listener interactions.

  • Tests successful task completion and listener notification
  • Validates exception handling and failure listener callbacks
  • Verifies listener removal functionality
  • Tests listener addition after task completion

Implementation Analysis

The testing approach utilizes JUnit and Mockito frameworks to validate task execution and listener behavior. The implementation employs mock objects to verify listener interactions and uses semaphores for synchronization testing.

Key patterns include:
  • Mock listener verification
  • Callable implementation for task execution
  • Asynchronous operation testing

Technical Details

Testing infrastructure includes:

  • JUnit 4 test framework
  • Mockito for mock object creation and verification
  • Semaphore for thread synchronization
  • MockitoRule for automatic mock initialization
  • Base test class extension for common functionality

Best Practices Demonstrated

The test suite exemplifies several testing best practices in async operation validation.

  • Proper mock usage and verification
  • Explicit test case isolation
  • Clear test method naming
  • Comprehensive error scenario coverage
  • Appropriate use of test annotations
  • Documentation of known issues (CI hanging)

airbnb/lottie-android

lottie/src/test/java/com/airbnb/lottie/LottieTaskTest.java

            
package com.airbnb.lottie;

import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import java.util.concurrent.Callable;
import java.util.concurrent.Semaphore;

import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;

public class LottieTaskTest extends BaseTest {

  @Mock
  public LottieListener<Integer> successListener;
  @Mock
  public LottieListener<Throwable> failureListener;

  @Rule
  public MockitoRule rule = MockitoJUnit.rule();

  @Test
  public void testListener() {
    new LottieTask<>(() -> new LottieResult<>(5), true)
        .addListener(successListener)
        .addFailureListener(failureListener);
    verify(successListener, times(1)).onResult(5);
    verifyNoInteractions(failureListener);
  }

  @Test
  public void testException() {
    final IllegalStateException exception = new IllegalStateException("foo");
    new LottieTask<>((Callable<LottieResult<Integer>>) () -> {
      throw exception;
    }, true)
        .addListener(successListener)
        .addFailureListener(failureListener);
    verifyNoInteractions(successListener);
    verify(failureListener, times(1)).onResult(exception);
  }

  /**
   * This hangs on CI but not locally.
   */
  @Ignore("hangs on ci")
  @Test
  public void testRemoveListener() {
    final Semaphore lock = new Semaphore(0);
    LottieTask<Integer> task = new LottieTask<>(new Callable<LottieResult<Integer>>() {
      @Override public LottieResult<Integer> call() {
        return new LottieResult<>(5);
      }
    })
        .addListener(successListener)
        .addFailureListener(failureListener)
        .addListener(new LottieListener<Integer>() {
          @Override public void onResult(Integer result) {
            lock.release();
          }
        });
    task.removeListener(successListener);
    try {
      lock.acquire();
    } catch (InterruptedException e) {
      throw new IllegalStateException(e);
    }
    verifyNoInteractions(successListener);
    verifyNoInteractions(failureListener);
  }

  @Test
  public void testAddListenerAfter() {
    LottieTask<Integer> task = new LottieTask<>(new Callable<LottieResult<Integer>>() {
      @Override public LottieResult<Integer> call() {
        return new LottieResult<>(5);
      }
    }, true);

    task.addListener(successListener);
    task.addFailureListener(failureListener);
    verify(successListener, times(1)).onResult(5);
    verifyNoInteractions(failureListener);
  }
}