You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义带Bitmap输入、AsyncTask回调的库的自动化测试及Mockito咨询

好问题!咱们一步步来解决你的疑问:

问题1:这类测试的可行性与实践方式

这类测试完全可行,而且是Android库开发中非常通用的实践——属于集成测试或功能测试的范畴,专门用来验证库在各种边界条件(异常输入、网络异常等)下的表现,确保鲁棒性。

关于测试位置:完全可以在库的测试目录中独立完成,不需要单独创建应用。Android Studio为库模块提供了两类测试目录,按需选择:

  • src/test/java:本地单元测试,运行在JVM上,速度快,适合测试纯逻辑,但无法直接模拟Android框架的真实运行环境(比如AsyncTask的异步调度)
  • src/androidTest/java:Instrumented测试,运行在真机或模拟器上,可以完整模拟Android系统环境、网络状态,适合测试依赖Android API的逻辑(比如你的AsyncTask执行、回调触发)
问题2:测试接口回调的示例代码(含Mockito用法)

首先需要在库的build.gradle中添加必要的测试依赖:

dependencies {
    // 本地单元测试依赖
    testImplementation 'org.mockito:mockito-core:4.8.1'
    testImplementation 'junit:junit:4.13.2'
    // Instrumented测试依赖(如果需要真机/模拟器测试)
    androidTestImplementation 'org.mockito:mockito-android:4.8.1'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
}

假设你的库核心类是ImageInfoManager,回调接口定义如下:

public interface ImageInfoCallback {
    void onSuccess(String imageInfo);
    void onFailure(Exception e);
}

下面分两种场景给出测试示例:

场景1:本地单元测试(模拟回调与逻辑)

本地测试中,我们用Mockito模拟回调接口和Bitmap,同时抽离AsyncTask的核心逻辑(避免依赖Android环境):

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import android.graphics.Bitmap;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class ImageInfoManagerTest {

    @Mock
    private ImageInfoCallback mockCallback; // 模拟回调对象
    @Mock
    private Bitmap mockBitmap; // 模拟Bitmap输入

    private ImageInfoManager manager = new ImageInfoManager();

    // 测试有效Bitmap输入的成功回调
    @Test
    public void testFetchImageInfo_ValidBitmap_TriggersSuccess() {
        // 执行测试方法
        manager.fetchImageInfo(mockBitmap, mockCallback);

        // 模拟AsyncTask执行完成并返回结果(这里假设你把网络请求逻辑抽成了可调用的方法)
        String mockInfo = "Sample: Landscape, 1920x1080";
        manager.onFetchCompleted(mockInfo); // 手动触发成功回调(根据你的库逻辑调整)

        // 验证回调的onSuccess被正确调用
        verify(mockCallback).onSuccess(mockInfo);
        // 确保onFailure没有被调用
        verify(mockCallback, never()).onFailure(any(Exception.class));
    }

    // 测试空Bitmap输入的失败回调
    @Test
    public void testFetchImageInfo_NullBitmap_TriggersFailure() {
        // 传入空Bitmap执行方法
        manager.fetchImageInfo(null, mockCallback);

        // 模拟空值校验失败的异常
        Exception error = new IllegalArgumentException("Bitmap cannot be null");
        manager.onFetchFailed(error);

        // 验证失败回调被触发
        verify(mockCallback).onFailure(error);
        verify(mockCallback, never()).onSuccess(anyString());
    }

    // 测试网络异常的失败回调
    @Test
    public void testFetchImageInfo_NetworkError_TriggersFailure() {
        manager.fetchImageInfo(mockBitmap, mockCallback);

        // 模拟网络请求失败
        Exception networkError = new RuntimeException("Network unavailable");
        manager.onFetchFailed(networkError);

        verify(mockCallback).onFailure(networkError);
        verify(mockCallback, never()).onSuccess(anyString());
    }
}

场景2:Instrumented测试(模拟真实网络状态)

如果需要测试真实Android环境下的网络禁用场景,可以用Instrumented测试配合Mockito模拟ConnectivityManager:

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.mockito.Mockito.*;

@RunWith(AndroidJUnit4.class)
public class ImageInfoManagerInstrumentedTest {

    @Mock
    private ImageInfoCallback mockCallback;
    private Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
    private ImageInfoManager manager = new ImageInfoManager(appContext);

    // 测试禁用网络时的回调
    @Test
    public void testFetchImageInfo_NoNetwork_TriggersFailure() throws InterruptedException {
        // 模拟无网络状态
        ConnectivityManager mockCm = mock(ConnectivityManager.class);
        when(mockCm.getActiveNetworkCapabilities()).thenReturn(null);
        // 替换系统的ConnectivityManager(如果你的库依赖它判断网络)
        // 注意:实际项目中可能需要用依赖注入来替换,避免直接依赖系统服务

        // 创建一个真实的Bitmap作为输入
        Bitmap testBitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
        manager.fetchImageInfo(testBitmap, mockCallback);

        // 用CountDownLatch等待异步回调触发
        CountDownLatch latch = new CountDownLatch(1);
        doAnswer(invocation -> {
            latch.countDown();
            return null;
        }).when(mockCallback).onFailure(any(Exception.class));

        // 等待最多5秒,确保回调执行完成
        latch.await(5, TimeUnit.SECONDS);

        // 验证失败回调被调用
        verify(mockCallback).onFailure(any(Exception.class));
    }
}

关键Mockito用法提示

  • @Mock:自动创建模拟对象,不用手动new
  • verify(mock).method(args):验证模拟对象的方法是否被调用(以及调用次数、参数是否正确)
  • when(mock.method()).thenReturn(result):给模拟对象的方法设定返回值
  • doAnswer():用来监听回调方法的调用,配合CountDownLatch处理异步逻辑

内容的提问来源于stack exchange,提问作者Sebastien Ferrand

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:16:18