Unit testing 单元测试一个已启动的服务,其中注入了几个字段?

Unit testing 单元测试一个已启动的服务,其中注入了几个字段?,unit-testing,android-service,robolectric,dagger,Unit Testing,Android Service,Robolectric,Dagger,我是个新手 TL;医生: 如果安卓服务使用Dagger将任何字段注入其中,那么为了实际执行注入,我需要该服务的一个实例 在Robolectric测试中,这对应于MyService service=roblectric.buildService(MyService.class).get()。然后,objectGraph.inject(服务) 然而,实际启动MyService的其余代码仍然使用context.startService(context,MyService.class) 问题:Dag

我是个新手

TL;医生:

  • 如果安卓
    服务
    使用Dagger将任何字段注入其中,那么为了实际执行注入,我需要该
    服务的一个实例
  • 在Robolectric测试中,这对应于
    MyService service=roblectric.buildService(MyService.class).get()
    。然后,
    objectGraph.inject(服务)
  • 然而,实际启动
    MyService
    的其余代码仍然使用
    context.startService(context,MyService.class)
问题:Dagger中解决这种不匹配的惯用方法是什么?


假设我有一个
服务
,如下所示:

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);
    }
}
在代码的其他地方,我有一个ApicClient类,它可以执行以下操作:

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
    }
}
但是,我在这里硬编码
TestModule


Dagger中有没有一个现成的习惯用法可以为这种情况设置依赖关系?

在测试或服务中硬编码模块是错误的。更好的方法是通过自定义的
应用程序
对象执行创建,该对象将依次保存singleton
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.
}

或者,您可以将最后一个方法重构成单独的类,并针对不同的风格或构建类型进行不同的实现。另外,您可能希望在
TestModule
的注释中设置
overrides=true

我仍然不明白。在您的示例中,第二个
onCreate()
MyService
中,对吗?如果我需要重写
getModules()
,那么我必须在我的应用程序代码和测试代码中创建一个不同的
MyService
子类。好的,在
src/main
src/test
中不能有同名的类。因此,服务的**类基本上成为
ApiClient
类的依赖项。这里缺少什么吗?没有。只有第一个代码块来自
MyService
。其他所有内容都来自您的自定义应用程序MyApp
。对于测试,您可以定义
MyTestApp
,它将扩展
MyApp
并覆盖
getModules
方法,只为测试添加匕首模块。啊-我现在明白您的意思了。我试试你的建议。谢谢。我意识到我的问题有很多问题。首先,作为测试的一部分,我试图实际启动一项服务——这不是Robolectric的目的。我把那个部分转移到一个单独的仪器测试中。我现在正在研究如何使用应用程序子类正确地实现依赖项注入部分。一旦我的东西开始工作,我会发布一个答案。@curioustechizen你到底需要什么帮助?我想我的回答中提到了一切,不是吗?
// 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.
}