Search code examples
javaunit-testingmockingmockitoandroid-lifecycle

Why are instances of a base class with Android dependencies unit testable?


I'm trying to understand why concrete instances of an abstract class with android dependencies on it are unit testable. Consider the following class:

import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.OnLifecycleEvent;

public abstract class BaseFoo implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void onResume() {
        ...
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void onCreate() {
        ...
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onDestroy() {
        ...
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void onPause() {
        ...
    }

    ...
}

And:

public class ConcreteFoo extends BaseFoo {

    public void bar() {
        ...
    }
}

The tests are based like this:

import android.arch.lifecycle.Lifecycle;

public abstract class BaseTest {

    @Mock protected Lifecycle lifecycle;

    ...
}

Considering the android dependencies on each class, why can something like:

@RunWith(MockitoJUnitRunner.class)
public class FooTest extends BaseTest {

    @Test
    public void testSomething() {
        ...
    }
}

be unit testable for ConcreteFoo? Is it only because the lifecycle is being mocked in BaseTest? If so, how can it really be tested as per real devices system callbacks? How to avoid a mistake in such tests? Is there something special in Mockito that allows this that other frameworks might not have?


Solution

  • "Classes with Android dependencies are not testable" seems to have quite a specific meaning.

    Take a classic Activity or Fragment. There is often a lot of dependency on TextView, LayoutManager etc. It is almost impossible to stub a behaviour on every single dependency to perform any kind of reasonable unit test. Note that the dependencies TextView etc. are android.* classes that are included in the runtime of a device running Android (i.e., your phone).

    Poorly-designed Presenter and ViewModel classes can also have this problem. If, for instance, the Presenter or ViewModel takes a dependency on a Context it will become difficult to put inside a test harness as the android.* Context class is difficult to mock.

    However, this directive does not necessarily apply to the android.arch.* classes. These are not included in the Android runtime on a phone and are designed in a way to facilitate testing. So in the example you have given, there doesn't seem to be a fault in including these in a class designed to be unit tested.