如何使用 Robolectric 编写 Android 服务单元测试

pro*_*typ 5 java android unit-testing android-service robolectric

在使用测试覆盖我的完整 Android 项目时,我试图找到一种方法来使用 Robolectric 测试我的服务。我需要测试的服务包含很多需要测试的逻辑。

经过一番研究,我从 robolectric找到了一个如何使用 shadowService 测试服务的代码示例,但我仍然不知道如何启动该服务并测试其功能。

首先,我想创建所有依赖项的模拟并将它们注入到服务中。然后对于每种方法,我想编写测试以及具有不同模拟依赖方法的服务。

我怎样才能做到这一点?测试 Android 服务时的最佳实践是什么?有人知道一些好的方法吗?

我的测试和服务到目前为止,但服务没有启动(调试器不会进入服务内的断点):

    @RunWith(RobolectricTestRunner.class)
    @Config(constants = BuildConfig.class , sdk = 23)
    public class MyServiceTest {
    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock
    Context context;
    @Mock
    ApiService apiService;
    @Mock
    ServiceManagerImpl serviceManager;

    @InjectMocks
    private MyService myService;

    private ShadowService shadowService;

    @Before
    public void setUp(){
        // explicit service creation from robolectric example
        this.updateService = Robolectric.setupService(UpdateService.class);

        this.shadowService = shadowOf(this.updateService);
        MockitoAnnotations.initMocks(this);
        when(this.apiService.fetchData()).thenReturn(42);
    }

    @Test
    public void testService(){
        this.updateService.startService(new Intent());
        // Test of a service function here ...
    }

}
Run Code Online (Sandbox Code Playgroud)

我的服务示例:

public class UpdateService extends Service {

    @Inject
    Context context;
    @Inject
    ApiService apiService;
    @Inject
    ServiceManager serviceManager;

    private List<ApkRelease> fetchedData;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        ((MyApplication) getApplication()).getComponent().inject(this);
        this.fetchData();
        return super.onStartCommand(intent, flags, startId);
    }

    private void fetchData() {
        Call<List<Data>> call = this.apiService.getData();
        call.enqueue(new Callback<List<Data>>() {
            @Override
            public void onResponse(Call<List<Data>> call, Response<List<Data>> response) {
                if (response.body() != null) {
                    if (!response.body().isEmpty()) {
                        fetcheData = response.body();
                        processNextData();
                    } else {
                        stopSelf();
                    }
                }
            }
            @Override
            public void onFailure(Call<List<Data>> call, Throwable t) {
                ACRA.getErrorReporter().handleException(t);
                startUpgrade();
            }
        });
    }

    private void processNextData() {
        if (this.fetchedData.isEmpty()) {
            startOtherStuff();
            return;
        }
        this.processData(this.fetchedReleases.remove(0));
    }

    private void processData(Data data, ResponseBody responseBody) {
        //process Data
        ....
        this.processNextData();
    }

    private void startOtherStuff() {
        Intent intent = new Intent(this.context, UpgradeService.class);
        this.context.startService(intent);
        this.stopSelf();
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)