Android 如何使用Robolectric测试IntentService?

Android 如何使用Robolectric测试IntentService?,android,android-testing,robolectric,Android,Android Testing,Robolectric,我正在尝试使用roblectric测试IntentService的onHandleIntent()方法 我以以下方式开始服务: Activity activity = new Activity(); Intent intent = new Intent(activity, MyService.class); activity.startService(intent); ShadowActivity shadowActivity = Robolectric.shadowOf(activity);

我正在尝试使用
roblectric
测试
IntentService
onHandleIntent()
方法

我以以下方式开始服务:

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
不为null,但似乎未调用
onHandleIntent()


我应该如何测试它呢?

我认为你的方法是错误的。 您正在这里测试:

Intent startedIntent = shadowActivity.getNextStartedService();
assertNotNull(startedIntent);
调用了
startService()

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

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

我不太清楚。。。但我认为你想写的测试只是测试Android框架。您应该编写两个测试

1) 测试是否调用了
startService()

2) 测试
onHandleIntent()
的行为(直接调用它)

我对
机器人分子
几乎没有经验,但希望这会有所帮助


Cheers

OnHandleContent
是一个受保护的方法,因此不能直接调用

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

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

我使用这个

@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());
}
然后我运行测试,一切正常

Robolectric有一个类似活动的服务生命周期。此控制器提供执行相应服务回调的所有方法(例如,
controller.attach().create().startCommand(0,0).destroy()

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

@RunWith(RobolectricGradleTestRunner.class)
公共类MyIntentService测试{
私人测试服务;
专用服务控制器;
@以前
公共作废设置(){
controller=roblectric.buildService(TestService.class);
service=controller.attach().create().get();
}
@试验
public void testWithIntent(){
Intent Intent=新的Intent(RuntimeEnvironment.application、TestService.class);
//向意图中添加额外内容
控制器.withIntent(intent).startCommand(0,0);
//在此断言
}
@之后
公共无效拆卸(){
controller.destroy();
}
公共静态类TestService扩展了MyIntentService{
启用公共布尔值=true;
@凌驾
公共无效启动(Intent Intent,int startId){
//与内部ServiceHandler.handleMessage()中的逻辑相同
//但运行在与服务相同的线程上
onHandleIntent(意图);
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;
    }
}
公共类IntentServiceController扩展ServiceController{
公共静态IntentService控制器buildIntentService(类serviceClass){
试一试{
返回新的IntentServiceController(Robolectric.getShadowsAdapter(),serviceClass);
}catch(IllegalAccessException |实例化exception e){
抛出新的运行时异常(e);
}
}
私有IntentServiceController(ShadowsAdapter ShadowsAdapter,类serviceClass)抛出IllegaAccessException、InstanceionException{
超级(暗影攻击机,服务级);
}
@凌驾
具有意图的公共意图服务控制器(意图){
超级。故意(故意);
归还这个;
}
@凌驾
公共意图服务控制器连接(){
super.attach();
归还这个;
}
@凌驾
public-IntentServiceController绑定(){
super.bind();
归还这个;
}
@凌驾
公共IntentServiceController创建(){
super.create();
归还这个;
}
@凌驾
公共意图服务控制器销毁(){
super.destroy();
归还这个;
}
@凌驾
公共意图服务控制器重新绑定(){
super.rebind();
归还这个;
}
@凌驾
公共IntentServiceController startCommand(int标志,int STARTTID){
super.startCommand(旗帜,startId);
归还这个;
}
@凌驾
公共意图服务控制器解除绑定(){
super.unbind();
归还这个;
}
公共意图服务控制员手册内容(){
调用whilepaused(“onHandleIntent”,getIntent());
归还这个;
}
}
Robolectric 3.1+ 创建服务的实例

直接呼叫HandleContent

    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
(而不是
.buildIntentService
)<据我所知,code>.buildIntentService
目前不工作

更新2017-05-17:


buildIntentService
的问题将是

您没有按照OP的要求测试服务的
onhandlecontent
。您正在测试活动是否调用service.Hi。我遵循了你的指导方针。我不需要在我的测试用例中编写服务,只需要包含一个调用OnHandleContent的公共方法,它也通过了我的意图。。而是让我的活动启动服务。。我只是实例化这个服务
    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);