Android:在检测测试中获取对已启动服务的引用
我正在尝试为我的Android:在检测测试中获取对已启动服务的引用,android,unit-testing,android-service,android-testing,Android,Unit Testing,Android Service,Android Testing,我正在尝试为我的NetworkMonitorService编写插装测试,如官方“”文档中所述 目前我陷入困境,因为我不知道如何获取对已启动服务的引用,以便将mock注入其中并断言行为 我的代码: @RunWith(AndroidJUnit4.class) @SmallTest public class NetworkMonitorServiceTest { @Rule public final ServiceTestRule mServiceTestRule = new Service
NetworkMonitorService
编写插装测试,如官方“”文档中所述
目前我陷入困境,因为我不知道如何获取对已启动服务的引用,以便将mock注入其中并断言行为
我的代码:
@RunWith(AndroidJUnit4.class)
@SmallTest
public class NetworkMonitorServiceTest {
@Rule public final ServiceTestRule mServiceTestRule = new ServiceTestRule();
@Test
public void serviceStarted_someEventHappenedInOnStartCommand() {
try {
mServiceTestRule.startService(new Intent(
InstrumentationRegistry.getTargetContext(),
NetworkMonitorService.class));
} catch (TimeoutException e) {
throw new RuntimeException("timed out");
}
// I need a reference to the started service in order to assert that some event happened
// in onStartCommand()...
}
}
该服务不支持绑定。我认为,如果我实现对绑定的支持,然后在测试中使用它,以便获得对服务的引用,它就可以工作。然而,我不喜欢仅仅为了支持测试用例而编写生产代码
那么,如何测试(仪器测试)一个不支持绑定的
服务呢?用特殊版本的“for tests”替换您的应用程序。通过提供定制的仪器测试运行程序来实现这一点。模拟您的依赖项,并将其用于此“测试应用程序”
下面是一个如何使用“测试应用程序”的简化示例。假设您希望在测试期间模拟网络层(例如,Api
)
public class App extends Application {
public Api getApi() {
return realApi;
}
}
public class MySerice extends Service {
private Api api;
@Override public void onCreate() {
super.onCreate();
api = ((App) getApplication()).getApi();
}
}
public class TestApp extends App {
private Api mockApi;
@Override public Api getApi() {
return mockApi;
}
public void setMockApi(Api api) {
mockApi = api;
}
}
public class MyTest {
@Rule public final ServiceTestRule mServiceTestRule = new ServiceTestRule();
@Before public setUp() {
myMockApi = ... // init mock Api
((TestApp)InstrumentationRegistry.getTargetContext()).setMockApi(myMockApi);
}
@Test public test() {
//start service
//use mockApi for assertions
}
}
在本例中,依赖项注入是通过应用程序的方法getApi
完成的。但是你也可以用Dagger或其他任何类似的方法。我找到了一个非常简单的方法。您只需执行一个绑定,就可以获得对已经运行的服务的引用,这与服务创建没有冲突,因为您已经使用onStartCommand
启动了它,如果您进行检查,您将看到onCreate
只调用了一次,因此您可以确保它是同一个服务。只需在示例之后添加以下内容:
Intent serviceIntent =
new Intent(InstrumentationRegistry.getTargetContext(),
NetworkMonitorService.class);
// Bind the service and grab a reference to the binder.
IBinder binder = mServiceRule.bindService(serviceIntent);
// Get the reference to the service
NetworkMonitorService service =
((NetworkMonitorService.LocalBinder) binder).getService();
// Verify that the service is working correctly however you need
assertThat(service, is(any(Object.class)));
我希望它能有所帮助。这至少对绑定服务有效:
@Test
public void testNetworkMonitorService() throws TimeoutException {
Intent intent = new Intent(InstrumentationRegistry.getTargetContext(), NetworkMonitorService.class);
mServiceRule.startService(intent);
IBinder binder = mServiceRule.bindService(intent);
NetworkMonitorService service = ((NetworkMonitorService.LocalBinder) binder).getService();
mServiceRule.unbindService();
}
要访问字段,请使用@VisibleForTesting(否则=VisibleForTesting.NONE)注释将应用程序替换为特殊版本的“for tests”。通过提供定制的仪器测试运行程序来实现这一点。模拟您的依赖项,并将其用于此“测试应用程序”。有关@MyDogTom的详细信息,我考虑过这种方法,但我看不到一种简单的方法可以通过这种方式模拟服务的依赖性。您能提供一些代码示例吗?谢谢,但我已经尝试过这种方法。这里的问题是,它不允许为每个测试注入模拟-您为所有将要运行的测试指定模拟。这可能适用于小型应用程序,但在大型应用程序中(我的Dagger 2对象图包含~10个模块中的数百个对象,由~5个组件使用),您希望做的是在每个测试中注入模拟。我找不到一个简单的方法来使用这种方法。是吗?@vasily我只在集成测试中使用这种方法,只模拟最大/最慢的依赖项(后端,应用层内)。对于单元测试,我将逻辑提取到单独的pojo(或几乎是pojo)类中,并为此类编写测试。在您的情况下,您的服务可以使用某种包含所有逻辑的帮助器。用一堆单元测试来覆盖这个助手。编写两个集成测试,以确保所有功能都能协同工作。在这一点上,您不需要涵盖所有情况。是的,这正是我现在所处的位置-我有针对“助手”和“经理”的单元测试,但我想对整个服务进行集成测试。然而,如果不模仿某些特定服务的依赖关系(至少我需要模仿Android的ConnectionManager、EventBus、ServerPingManager),我看不到编写集成测试的好方法。然而,我不想为所有测试模拟这些依赖关系——有些测试仍然应该使用非模拟依赖关系。无论如何,谢谢你的好主意+1.