如何对使用goAsync()的Android BroadcastReceiver进行单元测试
问题根因
goAsync() 方法只有在Android系统触发onReceive回调时才会返回有效的PendingResult实例,手动调用onReceive时没有系统注入的PendingResult上下文,所以默认返回null,导致空指针异常。
解决方案
可以通过Mock依赖、改造可测试性的方式完成单元测试,推荐使用Mockito作为Mock框架,具体实现如下:
方式1:优化原有代码可测试性后编写用例
先对原有Receiver做最小改造,把异步执行器提到成员变量,方便测试注入同步执行器,避免异步等待:
public class NotificationReceiver extends BroadcastReceiver { // 执行器改为成员变量,方便测试注入 private ExecutorService executor = Executors.newSingleThreadExecutor(); @VisibleForTesting void setExecutor(ExecutorService executor) { this.executor = executor; } public void onReceive(final Context context, final Intent intent) { final PendingResult asyncResult = goAsync(); asycTask(new Runnable() { @Override public void run() { handleAction(context, intent); // 耗时操作 asyncResult.finish(); } }); } private void asycTask(final Runnable task) { try { executor.execute(task); } catch (Throwable ex) {} } // handleAction等其他逻辑省略 }
对应的测试代码:
import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import android.content.BroadcastReceiver.PendingResult; @RunWith(MockitoJUnitRunner.class) public class NotificationReceiverTest { @Mock private Context application; @Mock private Intent intent; @Mock private PendingResult pendingResult; @Test public void testOnReceive() { NotificationReceiver receiver = new NotificationReceiver(); // 注入Mock的PendingResult,解决goAsync返回null的问题 receiver.setPendingResult(pendingResult); // 注入同步执行器,让异步任务直接在当前测试线程运行,无需等待 ExecutorService syncExecutor = new ExecutorService() { @Override public void execute(Runnable command) { command.run(); } // 其余未用到的接口方法可默认空实现 }; receiver.setExecutor(syncExecutor); // 调用待测试方法 receiver.onReceive(application, intent); // 直接验证逻辑,不需要额外等待 // 这里可以加handleAction相关的逻辑校验 // 验证finish方法被正确调用 verify(pendingResult, times(1)).finish(); } }
方式2:不改原有代码的测试方案
如果不想修改原有Receiver代码,可以通过Mockito的spy能力捕获异步任务,主动执行:
@Test public void testOnReceiveWithoutModifySource() { NotificationReceiver receiver = spy(new NotificationReceiver()); receiver.setPendingResult(pendingResult); // 捕获传入的异步任务 AtomicReference<Runnable> taskCaptor = new AtomicReference<>(); doAnswer(invocation -> { taskCaptor.set(invocation.getArgument(1)); return null; }).when(receiver).asycTask(any(ExecutorService.class), any(Runnable.class)); // 调用待测试方法 receiver.onReceive(application, intent); // 主动执行捕获到的异步任务 taskCaptor.get().run(); // 验证逻辑 verify(pendingResult, times(1)).finish(); }
内容的提问来源于stack exchange,提问作者lannyf
相关产品推荐
相关产品推荐

