单元测试已启动的服务,其中注入了少量字段?

时间:2015-01-08 13:25:13

标签: unit-testing android-service robolectric dagger

我是Dagger的新手。

TL; DR:

  • 如果Android Service使用Dagger将任何字段注入其中,那么为了实际执行注入,我需要有一个Service的实例。
  • 在Robolectric测试中,这对应于MyService service = Robolectric.buildService(MyService.class).get()。然后,objectGraph.inject(service);
  • 但是,实际启动的其余代码MyService仍使用context.startService(context, MyService.class);

问题:Dagger解决这种不匹配的惯用方法是什么?


假设我有Service如下:

public class MyService {
    @Inject Parser parser;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String data = intent.getStringExtra("data_to_be_parsed");
        parser.parse(data);
    }
}

在我的代码中,我有一个ApiClient类来执行此操作:

public class ApiClient{
    public static void parseInBackground(Context context, String data){
        //This service does not have its fields injected
        context.startService(new Intent(context, MyService.class).putExtra("data_to_be_parsed", data)); 
    }
}

将响应用户交互从活动中调用parseInBackground方法。

现在,我正在关注TDD,因此,我还没有为此编写应用程序模块。这是测试模块:

@Module(injects = MyService.class)
public class TestModule {
    @Provides @Singleton Parser provideParser(){
        return new MockParser();
    }
}

最后,测试用例:

@RunWith(Robolectric.class)
public class ApiTest {
    @Test
    public void parseInBackground_ParsesCorrectly(){
        //This service has its fields injected
        MyService service = Robolectric.buildService(MyService.class).get();
        ObjectGraph.create(new TestModule()).inject(service);

        ApiClient.parseInBackground(Robolectric.application, "<user><name>droid</name></user>");

        //Asserts here
    }
}

如您所见,在测试中,我检索服务的实例,然后将MockParser注入其中。但是,ApiClient类使用Intent直接启动服务。我没有机会进行注射。

我知道我可以MyService对自己进行注射:

public void onCreate(){
    ObjectGraph.create(new TestModule()).inject(this);
}

但是,我在这里硬编码TestModule

Dagger中是否存在为这种情况设置依赖关系的习惯用法?

1 个答案:

答案 0 :(得分:1)

在测试或服务中对模块进行硬编码的方法是错误的。更好的方法是通过自定义Application对象执行创建,而ObjectGraph对象又将保存单个// in MyService class @Override public void onCreate() { super.onCreate(); MyApp.from(context).inject(this); } // in MyApp class public static MyApp from(Context context) { return (MyApp) context.getApplicationContext(); } //... private ObjectGraph objectGraph; @Override public void onCreate() { // Perform Injection objectGraph = ObjectGraph.create(getModules()); objectGraph.inject(this); } public void inject(Object object) { objectGraph.inject(object); } protected Object[] getModules() { // return concrete modules based on build type or any other conditions. } 对象。例如:

overrides=true

或者,您可以将最后一个方法重构为单独的类,并针对不同的flavor或构建类型进行不同的实现。您也可以在TestModule的注释中设置{{1}}。