19

我正在尝试测试using的onHandleIntent()方法。IntentServiceRobolectric

我开始服务:

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);

似乎startedIntent不是空的,但onHandleIntent()似乎没有被调用。

我应该如何测试它?

4

5 回答 5

14

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

理论上我们可以预期IntentService.onStartCommand()会触发 IntentService.onHandleIntent(Intent),通过它的内部Handler。但是,这Handler使用Looper在后台线程上运行的 a,我不知道如何使该线程前进到下一个任务。一种解决方法是创建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);
        }
    }
}

更新:或者,为 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;
    }
}
于 2015-11-17T11:13:52.207 回答
6

onHandleIntent是受保护的方法,因此不能直接调用。

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

然后onHandleIntent直接从测试用例调用。

于 2012-09-29T15:44:20.817 回答
2

Robolectric 3.1+

创建服务实例

直接调用 onHandleIntent

    YourService svc = Robolectric.buildService(YourService.class).get();
    Intent fakeIntent = new Intent();
    fakeIntent.putExtra(YourService.SOME_EXTRA, "debug|fail");

    svc.onHandleIntent(fakeIntent);

    assertThat(something.happened).isEqualTo(true);

注意:即使您正在测试 IntentService,也要 使用Robolectric.buildService(not )。据我所知,目前无法正常工作。.buildIntentService.buildIntentService

2017 年 5 月 17 日更新:

问题buildIntentService在未来修复

于 2017-05-11T17:07:39.737 回答
1

我认为您以错误的方式处理了这个问题。你在这里测试:

Intent startedIntent = shadowActivity.getNextStartedService();
assertNotNull(startedIntent);

startService()称为。

然后,您必须假设 Android 已经正确实现了它们的结束,并且在调用startService()服务之后确实会启动。

我认为如果你想测试 的行为onHandleIntent(),你必须直接调用它??

我不确定……但我认为您要编写的测试只是测试 Android 框架。你应该写两个测试。

1)startService()被调用的测试(就像你在你的例子中一样)。

2)测试onHandleIntent()(通过直接调用)的行为。

我的经验很少,Robolectric但希望这会有所帮助。

干杯

于 2012-09-29T11:25:41.200 回答
1

我用这个

@Before
public void setup(){
    mainActivity = Robolectric.buildActivity(MainActivity.class)
            .create()
            .start()
            .resume()
            .get();
    context = Robolectric.getShadowApplication().getApplicationContext();
    bt_start = (Button) mainActivity.findViewById(R.id.button);
    bt_stop = (Button) mainActivity.findViewById(R.id.button2);
}

@Test
public void serviceIsOn(){

    bt_start.performClick();
    Intent intent = Robolectric.shadowOf(mainActivity).peekNextStartedService();
    assertEquals(MiService.class.getCanonicalName(),intent.getComponent().getClassName());
}

然后我运行测试,一切正常

于 2014-02-10T20:39:41.823 回答