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

被测代码在onCreate()中运行时,如何使用Idling Resource?

问题描述

我正在学习在Espresso测试中使用Idling Resource,但遇到了一个棘手的问题:待测试的代码在Activity的onCreate()方法里就直接执行了。之前看到有人建议在@Before注解的方法里注册Idling Resource,但这根本行不通——因为注册需要拿到Activity实例,可启动Activity时onCreate()(甚至onStart()、onResume())已经跑完了,此时Idling Resource还没注册上:

private ActivityScenario<MyActivity> scenario;
private IdlingResource myIdlingResource;

@Before
public void setUp() {
    scenario = ActivityScenario.launch(MyActivity.class); // 这里已经执行完onCreate等生命周期方法,Idling Resource还是null
    scenario.onActivity(activity -> { 
        // 现在拿到Activity实例,但已经晚了;这个方法没法在Activity启动前调用
        myIdlingResource= activity.getIdlingResource();
        IdlingRegistry.getInstance().register(myIdlingResource);
    });
}

@Test
public void test() {
    // 断言Activity创建时发生的操作
}

这种情况下该怎么正确使用Idling Resource?


解决方案

方案1:让Activity在测试模式下等待Idling Resource注册

修改你的MyActivity,加入测试模式判断,延迟执行onCreate里的耗时任务,直到Idling Resource注册完成:

  1. 在Activity中添加测试用的同步锁:
public class MyActivity extends AppCompatActivity {
    // 仅用于测试的同步锁
    @VisibleForTesting
    public static CountDownLatch testLatch;
    private MyIdlingResource idlingResource;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        
        idlingResource = new MyIdlingResource();
        
        // 如果是测试模式,先等待信号再执行耗时任务
        if (testLatch != null) {
            try {
                testLatch.await();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        
        // 原来的耗时任务逻辑
        startLongRunningTask();
    }

    @VisibleForTesting
    public IdlingResource getIdlingResource() {
        return idlingResource;
    }

    private void startLongRunningTask() {
        // 耗时操作开始时标记非空闲
        idlingResource.setIdleState(false);
        // 你的耗时逻辑(比如网络请求、异步任务)
        // ...
        // 耗时操作结束时标记空闲
        idlingResource.setIdleState(true);
    }
}
  1. 在测试类中先初始化锁,启动Activity后注册Idling Resource再释放锁:
private ActivityScenario<MyActivity> scenario;
private IdlingResource myIdlingResource;

@Before
public void setUp() {
    // 初始化锁,让Activity的onCreate暂时阻塞
    MyActivity.testLatch = new CountDownLatch(1);
    
    // 启动Activity,此时onCreate会卡在await()处
    scenario = ActivityScenario.launch(MyActivity.class);
    
    // 拿到Activity实例,注册Idling Resource后释放锁
    scenario.onActivity(activity -> { 
        myIdlingResource = activity.getIdlingResource();
        IdlingRegistry.getInstance().register(myIdlingResource);
        MyActivity.testLatch.countDown();
    });
}

@After
public void tearDown() {
    if (myIdlingResource != null) {
        IdlingRegistry.getInstance().unregister(myIdlingResource);
    }
    // 重置锁,避免影响其他测试用例
    MyActivity.testLatch = null;
}

@Test
public void testActivityCreation() {
    // Espresso会自动等待Idling Resource变为空闲,此时可以安全断言
    onView(withId(R.id.result_text)).check(matches(withText("预期结果")));
}

方案2:使用全局静态Idling Resource

把Idling Resource做成全局可访问的实例,在测试启动前就注册,让Activity直接引用这个实例:

  1. 创建全局Idling Resource类:
public class GlobalIdlingResource implements IdlingResource {
    private static GlobalIdlingResource instance;
    private ResourceCallback callback;
    private boolean isIdle = true;

    public static GlobalIdlingResource getInstance() {
        if (instance == null) {
            instance = new GlobalIdlingResource();
        }
        return instance;
    }

    @Override
    public String getName() {
        return GlobalIdlingResource.class.getName();
    }

    @Override
    public boolean isIdleNow() {
        return isIdle;
    }

    @Override
    public void registerIdleTransitionCallback(ResourceCallback callback) {
        this.callback = callback;
    }

    public void setIdleState(boolean isIdle) {
        this.isIdle = isIdle;
        if (isIdle && callback != null) {
            callback.onTransitionToIdle();
        }
    }
}
  1. 在Activity的onCreate中直接使用全局实例:
public class MyActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        
        GlobalIdlingResource idlingResource = GlobalIdlingResource.getInstance();
        idlingResource.setIdleState(false);
        
        // 模拟耗时任务
        new Handler(Looper.getMainLooper()).postDelayed(() -> {
            // 任务完成,标记为空闲
            idlingResource.setIdleState(true);
            // 更新UI等操作
        }, 3000);
    }
}
  1. 在测试类中先注册全局Idling Resource,再启动Activity:
private IdlingResource globalIdlingResource;

@Before
public void setUp() {
    // 先注册全局Idling Resource
    globalIdlingResource = GlobalIdlingResource.getInstance();
    IdlingRegistry.getInstance().register(globalIdlingResource);
    
    // 再启动Activity,此时onCreate里的任务会关联到已注册的Idling Resource
    ActivityScenario.launch(MyActivity.class);
}

@After
public void tearDown() {
    if (globalIdlingResource != null) {
        IdlingRegistry.getInstance().unregister(globalIdlingResource);
    }
}

@Test
public void testActivityCreation() {
    onView(withId(R.id.result_text)).check(matches(withText("预期结果")));
}

方案3:替换线程池为IdlingThreadPoolExecutor

如果你的耗时任务是通过线程池执行的,可以直接用Espresso提供的IdlingThreadPoolExecutor替换原线程池,无需修改Activity核心代码:

  1. 在测试类中创建并注册Idling线程池:
private IdlingThreadPoolExecutor idlingExecutor;

@Before
public void setUp() {
    // 创建带Idling能力的线程池
    ExecutorService delegate = Executors.newSingleThreadExecutor();
    idlingExecutor = new IdlingThreadPoolExecutor("TestExecutor", delegate);
    IdlingRegistry.getInstance().register(idlingExecutor);
    
    // 把线程池注入到Activity(可以用依赖注入,或者静态方法替换)
    MyActivity.setExecutor(idlingExecutor);
    
    // 启动Activity
    ActivityScenario.launch(MyActivity.class);
}

@After
public void tearDown() {
    if (idlingExecutor != null) {
        IdlingRegistry.getInstance().unregister(idlingExecutor);
        idlingExecutor.shutdown();
    }
}
  1. 在Activity中使用注入的线程池执行任务:
public class MyActivity extends AppCompatActivity {
    private static ExecutorService executor = Executors.newSingleThreadExecutor();

    @VisibleForTesting
    public static void setExecutor(ExecutorService executor) {
        MyActivity.executor = executor;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        
        executor.execute(() -> {
            // 耗时操作逻辑
            runOnUiThread(() -> {
                // 更新UI
            });
        });
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 03:15:47