如何使用Robolectric测试IntentService?

Gal*_*aim 18 android robolectric android-testing

我想要测试onHandleIntent()的方法IntentService使用Robolectric.

我正在启动服务:

Activity activity = new Activity();
Intent intent = new Intent(activity, MyService.class);
activity.startService(intent);

ShadowActivity shadowActivity = Robolectric.shadowOf(activity);
Intent startedIntent = shadowActivity.getNextStartedService();
assertNotNull(startedIntent);
Run Code Online (Sandbox Code Playgroud)

似乎startedIntent不是null,但onHandleIntent()似乎没有被调用.

我该怎么测试呢?

hid*_*dro 11

Robolectric ServiceController可以像活动一样通过服务生命周期.该控制器提供执行相应服务回调的所有方法(例如controller.attach().create().startCommand(0, 0).destroy()).

从理论上讲,我们可以预期IntentService.onStartCommand()IntentService.onHandleIntent(Intent)通过其内部触发 Handler.然而,这Handler使用了一个Looper在后台线程上运行的,我不知道如何让这个线程前进到下一个任务.解决方法是创建TestService模仿相同的行为,但onHandleIntent(Intent)在主线程(用于运行测试的线程)上触发.

@RunWith(RobolectricGradleTestRunner.class)
public class MyIntentServiceTest {
    private TestService service;
    private ServiceController<TestService> controller;

    @Before
    public void setUp() {
        controller = Robolectric.buildService(TestService.class);
        service = controller.attach().create().get();
    }

    @Test
    public void testWithIntent() {
        Intent intent = new Intent(RuntimeEnvironment.application, TestService.class);
        // add extras to intent
        controller.withIntent(intent).startCommand(0, 0);
        // assert here
    }

    @After
    public void tearDown() {
        controller.destroy();
    }

    public static class TestService extends MyIntentService {
        public boolean enabled = true;

        @Override
        public void onStart(Intent intent, int startId) {
            // same logic as in internal ServiceHandler.handleMessage()
            // but runs on same thread as Service
            onHandleIntent(intent);
            stopSelf(startId);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:或者,为IntentService创建类似的控制器非常简单,如下所示:

public class IntentServiceController<T extends IntentService> extends ServiceController<T> {
    public static <T extends IntentService> IntentServiceController<T> buildIntentService(Class<T> serviceClass) {
        try {
            return new IntentServiceController<>(Robolectric.getShadowsAdapter(), serviceClass);
        } catch (IllegalAccessException | InstantiationException e) {
            throw new RuntimeException(e);
        }
    }

    private IntentServiceController(ShadowsAdapter shadowsAdapter, Class<T> serviceClass) throws IllegalAccessException, InstantiationException {
        super(shadowsAdapter, serviceClass);
    }

    @Override
    public IntentServiceController<T> withIntent(Intent intent) {
        super.withIntent(intent);
        return this;
    }

    @Override
    public IntentServiceController<T> attach() {
        super.attach();
        return this;
    }

    @Override
    public IntentServiceController<T> bind() {
        super.bind();
        return this;
    }

    @Override
    public IntentServiceController<T> create() {
        super.create();
        return this;
    }

    @Override
    public IntentServiceController<T> destroy() {
        super.destroy();
        return this;
    }

    @Override
    public IntentServiceController<T> rebind() {
        super.rebind();
        return this;
    }

    @Override
    public IntentServiceController<T> startCommand(int flags, int startId) {
        super.startCommand(flags, startId);
        return this;
    }

    @Override
    public IntentServiceController<T> unbind() {
        super.unbind();
        return this;
    }

    public IntentServiceController<T> handleIntent() {
        invokeWhilePaused("onHandleIntent", getIntent());
        return this;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,从Robolectric 3.4开始,“ attach()”调用是隐式的,并且[这些方法已标记为“私有”](http://robolectric.org/migrating/#activitycontrollerattach)。相反,[创建控制器时会自动附加服务](https://github.com/robolectric/robolectric/blob/master/shadows/framework/src/main/java/org/robolectric/android/controller/ServiceController。 java#L18)。 (2认同)

Gal*_*aim 6

onHandleIntent 是一种受保护的方法,因此无法直接调用它.

我的解决方案是在我的测试用例中扩展服务类,覆盖onHandleIntent使其公开和调用super.onHandleIntent(intent)

然后onHandleIntent直接从测试用例中调用.