Hot questions for Using Mockito in dagger 2

Hot questions for Using Mockito in dagger 2

Question:

I have a heavily dependency injected (dagger2) application. I would like to run an espresso test without having the test navigate through the whole application, and log into the application.

I would like to start on my teleActivity, and mock the login manager. However in any @test function, we have already hit the null pointer as we have called onCreate. If I override it before we launch the activity (show below) the activity is null.

To my understanding, the ability to switch our underlining dependencies is a large reason why we use Dagger2, else it would be just a very over engineered singleton. How do I override, mock, or switch the injection to a testing dagger module -- so I can create this simple espresso test.

Note I also wrote all this in the MVP design pattern if that makes a difference.

TeleActivity

@Inject
TelePresenter mTelePresenter;
@Inject
public LoginStateManager mLoginStateManager;

    @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ButterKnife.bind(this);
    DaggerInjectorTele.get().inject(this);
    mTelePresenter.setTeleDependencies(this);
    Intent intent = getIntent();

    String searchId = null;

    if (intent != null) {
        searchId = intent.getStringExtra(Constants.SEARCH_ID);
       }

    mTelePresenter.onCreateEvent(searchId,
            Helper.makeAuthorizationHeader(
            // CRASH Null pointer
            mLoginStateManager.getBaseLoginResponse().getAccessToken()));

}

Espresso

@LargeTest
@RunWith(AndroidJUnit4.class)
public class TeleTest {
    @Rule
    public ActivityTestRule<TeleActivity> mActivityTestRule = new ActivityTestRule(
            TeleActivity.class) {
        @Override
        protected void beforeActivityLaunched() {
            super.beforeActivityLaunched();
            TeleActivity teleActivity = (TeleActivity)getActivity();
             //teleActivity NULL!
            teleActivity.mLoginStateManager = mock(LoginStateManager.class);
            LoginResponse loginResponse = mock(LoginResponse.class);
            when(loginResponse.getAccessToken()).thenReturn("1234");
            // Nope here still null


when(teleActivity.mLoginStateManager.getBaseLoginResponse()).thenReturn(loginResponse);

        }
    };

Dagger Injector

  public class DaggerInjectorTele {
    private static TelePresenterComponent telePresenterComponent =
            DaggerTelePresenterComponent.builder().build();

    public static TelePresenterComponent get() {
        return telePresenterComponent;
    }
}

TelePresenterComponent

@Singleton
@Component(modules = {TelePresenterModule.class,
        LoginStateManagerModule.class})
public interface TelePresenterComponent {
    void inject(TeleActivity activity);
}

TelePresenterModule

@Module
public class TelePresenterModule {

    @Provides
    @Singleton
    public TelePresenter getTelePresenter() {
        return new TelePresenter();
    }
}

LoginStateManagerModule

@Module
public class LoginStateManagerModule {

    @Provides
    @Singleton
    public LoginStateManager getLoginStateManager(){
        return new LoginStateManager();
    }
}

Answer:

First, your decision to use dependency injection (Dagger2) is a very good one and will indeed make your tests easier to write.

You have to override dependency injection configuration (module) and inject a mock. Here is a simple example of how it can be done.

First you need a mock:

LoginStateManager lsmMock = mock(LoginStateManager.class);

Now override the DI config to use this mock:

//Extend your TelePresenterModule, override provider method
public class TestTelePresenterModule extends TelePresenterModule{
    @Override
    public LoginStateManager getLoginStateManager() {
        //simply return the mock here
        return lsmMock;
    }
}

Now to the test:

@Test
//this is an espresso test
public void withAMock() {
    //build a new Dagger2 component using the test override
    TelePresenterComponent componentWithOverride = DaggerTelePresenterComponent.builder()
            //mind the Test in the class name, see a class above
            .telePresenterModule(new TestTelePresenterModule())
            .build();

    //now we initialize the dependency injector with this new config
    DaggerInjectorTele.set(componentWithOverride);

    mActivityRule.launchActivity(null);

    //verify that injected mock was interacted with
    verify(lsmMock).whatever();
}

Example from: https://github.com/yuriykulikov/DIComparison/blob/master/app/src/androidTest/java/com/example/yuriy/dependencyinjectioncomparison/Dagger2Test.java

Question:

I'm trying to write some tests for fragments which have fields annotated with @Inject. For example, a chunk of my app looks like this:

Module:

@Module
public class PdfFactoryModule {

    @Provides @Singleton
    PdfFactory providePdfFactory() {
        return PdfFactory.getPdfFactory();
    }
}

Component:

@Singleton
@Component(modules = PdfFactoryModule.class)
public interface CorePdfComponent {
    void inject(PagerFragment pagerFragment);
}

Application:

public class CorePdfApplication extends Application {
    @NonNull
    private CorePdfComponent component;

    @Override
    public void onCreate() {
        super.onCreate();
        component = DaggerCorePdfComponent.builder().build();
    }

    @NonNull
    public CorePdfComponent getComponent() {
        return component;
    }

}

PagerFragment:

public class PagerFragment extends Fragment {
@Inject PdfFactory pdfFactory;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //Dagger 2
    ((CorePdfApplication) getActivity().getApplication()).getComponent().inject(this);
}

(Note that these are only snippets of my whole code, I'm showing only the essentials for this particular dependency to keep it clear.)


I was trying to do a test like this:

Fake Module:

@Module
public class FakePdfFactoryModule extends PdfFactoryModule {

    @Override
    PdfFactory providePdfFactory() {
        return Mockito.mock(PdfFactory.class);
    }
}

Fake Component:

@Singleton
@Component(modules = FakePdfFactoryModule.class)
public interface FakeCorePdfComponent extends CorePdfComponent {
    void inject(PagerFragmentTest pagerFragmentTest);
}

Fake Application:

public class FakeCorePdfApplication extends CorePdfApplication {
    @NonNull
    private FakeCorePdfComponent component;

    @Override
    public void onCreate() {
        super.onCreate();
        component = DaggerFakeCorePdfComponent.builder().build();
    }

    @NonNull
    public FakeCorePdfComponent getComponent() {
        return component;
    }
}

Test:

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21, application = FakeCorePdfApplication.class)
public class PagerFragmentTest {

    PagerFragment pagerFragment;

    @Before
    public void setup() {
        pagerFragment = new PagerFragment();
        startVisibleFragment(pagerFragment);
    }

    @Test
    public void exists() throws Exception {
        assertNotNull(pagerFragment);
    }

But the DaggerFakeCorePdfComponent doesn't generate. I may have messed up big time because I never tested with dependency injection. What am I doing wrong?


Answer:

My advice - "Do not use dagger in tests".

Just change your code to next:

public class FakeCorePdfApplication extends CorePdfApplication {
    @NonNull
    private CorePdfComponent component = mock(CorePdfComponent.class);

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @NonNull
    public CorePdfComponent getComponent() {
        return component;
    }
}

And:

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21, application = FakeCorePdfApplication.class)
public class PagerFragmentTest {

    PagerFragment pagerFragment;

    @Before
    public void setup() {
        pagerFragment = new PagerFragment();
        CorePdfComponent component = ((CorePdfApplication)RuntimeEnvironment.application).getComponent();

        doAnswer( new Answer() {
          Object answer(InvocationOnMock invocation) {
           fragment. pdfFactory = mock(PdfFactory.class);
           return null;
          }
        }).when(component).inject(pageFragment);
        startVisibleFragment(pagerFragment);
    }

    @Test
    public void exists() throws Exception {
        assertNotNull(pagerFragment);
    }
}

Question:

I am using Dagger 2 and have a few issues with generate singleton providers in the module when implementing tests for my class.

class SomeContentProvider extends ContentProvider {

    // this should be normal foo if run by application, 
    // or mocked foo if run by tests
    @Inject Foo foo;

    public Provider() {
        Component.getComponent().inject(this);
    }
}

@Module
class ProviderModule {
    @Singleton
    @Provides
    Foo providesFoo() {
        returns new Foo();
    }
}

@Module
class ProviderTestModule {
    @Singleton
    @Provides
    Foo providesFoo() {
        returns Mockito.mock(Foo.class);
    }
}


public class SomeContentProviderTests extends InstrumentationTestCase {

    @Inject Foo foo; // this should be mocked Foo

    @Override
    public void setUp() throws Exception {
        super.setUp();

        MockitoAnnotations.initMocks(this);

        Component.getTestComponent().inject(this);
    }

    public void test1() {
        // needs new instance of Foo when test1 is run from SomeContentProvider
    }

    public void test2() {
        // needs another new instance of Foo when test2 is run from SomeContentProvider
    }
}

So I have 2 questions.

  1. I cannot use constructor injection as ContentProvider has a default constructor. How does SomeContentProvider get the Foo from the the test module?

  2. In test1 and test2, how do I ensure that a new instance of Foo is created when each test is being run?

Thanks!


Answer:

I found this post especially useful for me. Though the particular problem I am have is much more convoluted - but I had used the same idea to mock the module's provider.

How do you override a module/dependency in a unit test with Dagger 2.0?

Question:

I have the following LoginFragment that uses Dagger to inject its fields:

class LoginFragment : DaggerFragment() {

    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        viewModel = ViewModelProviders.of(this, viewModelFactory)
                .get(LoginViewModel::class.java)
    }

I also have a corresponding test that mocks the LoginViewModel according to the documentation from Google: "You can create the fragment and provide it a mock ViewModel."

@MediumTest
@RunWith(AndroidJUnit4::class)
class LoginFragmentTest {

    @Mock
    private lateinit var viewModel: LoginViewModel

    @Before
    fun setUp() {
        loginFragment = LoginFragment()

        loginFragment.viewModelFactory = createMockViewModelFactory(viewModel)

        activityRule.activity.setFragment(loginFragment)
    }
}

The problem is that when the onAttached method of the fragment is invoked, Dagger overrides the viewModelFactory with its own object, thus replacing my mock.

How can I prevent Dagger from overriding my mock object?


Answer:

In the android-architecture-components samples on Github Google have an interesting solution.

They inject the activities trough ActivityLifecycleCallbacks. For instrumented tests they use a TestApp that does not register ActivityLifecycleCallbacks so it injects nothing.

Just like in your example the ViewModel.Factory is package private so in the test you can assign it yourself.

For Fragments there is FragmentManager.FragmentLifecycleCallbacks class which can be used. Instead of the Fragment injecting itself in onActivityCreated your production activity injects the Fragment using FragmentLifecycleCallbacks. You can create a testing activity which does not inject the fragment and you can create a mock factory yourself.

Question:

I have some trouble trying to figure out how to write unit tests for an AWS Lambda handler. Here is an example:

App.kt is the AWS lambda handler class.

class App : RequestHandler<Any, Any> {
    private val component: AppComponent by lazy {
        DaggerAppComponent.create()
    }
    override fun handleRequest(input: Any, context: Context): Any {
        component.getBusinessLogic().doSomething()
        // code
    }
}

AppComponent.kt is the Dagger2 component to get the business logic.

@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {
    fun getBusinessLogic(): BusinessLogic
}

AppModule.kt is the Dagger2 module.

@Module
abstract class AppModule {
    @Provides
    @Singleton
    fun provideAmazonECS(): AmazonECS = AmazonECSClient.builder().build()
}

BusinessLogic.kt is the business logic for the AWS lambda function

class BusinessLogic @Inject constructor(...) {
    fun doSomething(): Any{
        // code to use AmazonECS
    }
}

Now, here is how I would like to test App.kt by mocking the BusinessLogic class.

AppTest.kt

class AppTest {
    @Test
    fun `test something`() {
        val mockLogic = mock(BusinessLogic::class.java)
        val app = App()
        `when`(mockLogic.doSomething()).thenReturn(...)

        app.handleRequest(...)
    }
    @Test
    fun `test exception`() {
        val mockLogic = mock(BusinessLogic::class.java)
        val app = App()
        `when`(mockLogic.doSomething()).thenThrow(...)

        assertThrows<...> { app.handleRequest(...) }
    }
}

So, my trouble is that I'm not sure how to use a mocked BusinessLogic instance. The App class must have an empty constructor for AWS lambda to instantiate. Modifying the code such as creating a secondary constructor just for unit testing does not feel right.

Thanks in advance and I appreciate any help with this issue.


Answer:

Thanks to @Dominik G. for the comments and here is a solution based on the conversation.

App.kt is modified so that there is only one line and as such do not need to be unit tested.

class App : RequestHandler<Any, Any> {
    private val component: AppComponent by lazy {
        DaggerAppComponent.create()
    }
    override fun handleRequest(input: Any, context: Context): Any {
        return component.getLambdaLogic().handleRequest(input, context)
    }
}

LambdaLogic.kt now holds the logic to specifically for AWS lambda.

class LambdaLogic @Inject constructor(businessLogic: BusinessLogic) {
    fun handleRequest(input: Any, context: Context): Any {
        businessLogic.doSomething()
        // code
    }
}

AppComponent.kt is modified to return the LambdaLogic instead of the BusinessLogic.

@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {
    fun getLambdaLogic(): LambdaLogic
}

LambdaLogicTest.kt tests the AWS lambda logic.

class LambdaLogicTest{
    @Test
    fun `test something`() {
        val mockBusinessLogic = mock(BusinessLogic::class.java)
        val lambdaLogic = LambdaLogicTest(mockBusinessLogic)
        `when`(mockBusinessLogic.doSomething()).thenReturn(...)

        lambdaLogic.handleRequest(...)
    }
    @Test
    fun `test exception`() {
        val mockBusinessLogic = mock(BusinessLogic::class.java)
        val lambdaLogic = LambdaLogicTest(mockBusinessLogic)
        `when`(mockBusinessLogic .doSomething()).thenThrow(...)

        assertThrows<...> { lambdaLogic.handleRequest(...) }
    }
}

Using this approach, there is no longer a real need to unit test App.kt and we can also unit tests the AWS lambda logic.

Question:

I am using very simple and likely very common scenario. Here is my sample dependency:

public class MyDependency {
   @Inject
   public MyDependency(...) {
      ...
   }
}

I am not listing the above in any module (that is, there is no @Provides for MyDependency).

My sample use case goes like this:

public class ThePresenter {
   @Inject
   MyDependency myDependency;

   public ThePresenter() {
      App.getInstance().getAppComponent().inject(this);
   }
}

Now I'd like to mock my dependency in unit tests. I don't want to use modules overrides (that would mean I have to add @Provides for all my dependencies marked with @Inject constructors), test components etc. Is there any alternative but standard and simple approach for the problem?


Answer:

You need to use constructor injection, rather than your injection site inside the Presenter class constructor. Expose your Presenter to dagger2 by adding the @Inject annotation on the constructor (like you have done with the dependency):

public class ThePresenter {

   private final MyDependency myDependency;

   @Inject public ThePresenter(MyDependency myDependency) {
      this.myDependency = myDependency;
   }
}

This then allows inversion of control and supplying the dependency/mock.

Usage :

public class ThePresenterTest {

   @Mock private MyDependency myDependency;

   private ThePresenter presenter;

   @Before public void setup() {
      MocktioAnnotations.initMocks(this);
      presenter = new ThePresenter(myDependency);

      Mockito.when(myDependency.someMethod()).thenReturn(someValue);
      ....
   }
}

Question:

I am trying to mock data with Mockito and getting NPE. Here is the sample code I am trying to test:

public class MyPresenter implements Contract.Presenter {
  @Inject
  MyManager myManager;

  @Override
  public void doSomething(Data data) {
    myManager.doSomething(data);
  }
}

public class MyPresenterTest {

  @Mock
  private MyManager myManager; 

  @InjectMocks
  private MyPresenter myPresenter;

  @Before
  public void setup(){
  MockitoAnnotations.initMocks(this);
  }

  @Test
  public void doSomethingTest(){
    Data data = new Data();
    myPresenter.doSomething(data);
    verify(myManager).doSomething(data);
  }
}

Answer:

Mockito injects mocks only to the constructor, leaving the fields undefined. In order to test it properly, one needs to initialize the class with the constructor parameter and Mockito by hand. It will work if you can add a constructor which takes in MyManager instance such as -

public class MyPresenter implements Contract.Presenter {
  @Inject
  MyManager myManager;

  public MyPresenter(MyManager myManager){ // constructor is required for mocikto to inject your fields.
     this.myManager = manager
  }

  @Override
  public void doSomething(Data data) {
     myManager.doSomething(data);
  }
}

Question:

I use Dagger 2 to provide all my classes in Android app. I want to unit test my repository class which has Retrofit service in the constructor.

class MainRepository(private val service: ImageService) {

fun download(username: String, liveData: MutableLiveData<ImageDownloadResult>) {
    service.downloadImage(username).enqueue(object : Callback<ImageResponse> {
        override fun onFailure(call: Call<ImageResponse>, t: Throwable) {
            liveData.value = DownloadFailed(t)
        }

        override fun onResponse(call: Call<ImageResponse>, response: Response<ImageResponse>) {
            if (response.isSuccessful) {
                response.body()?.let {
                    liveData.value = DownloadSuccessful(it.image)
                }
            } else {
                liveData.value = DownloadFailed(
                    when (response.code()) {
                        401 -> Unauthorized()
                        403 -> Forbidden()
                        404 -> NotFound()
                        in 500..599 -> InternalServerError()
                        else -> General()
                    }
                )
            }
        }
    })
}

}

Network module class provides the service in this way:

@Provides
@Singleton
fun provideImageService(gson: Gson, okHttpClient: OkHttpClient): ImageService {
    return Retrofit.Builder()
        .addConverterFactory(GsonConverterFactory.create(gson))
        .baseUrl(mBaseUrl)
        .client(okHttpClient)
        .build()
        .create(ImageService::class.java)
}

I am trying to use Mockito to mock the classes, but I get NullPointerException in the download method.

public class MainRepositoryTest {

@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
@InjectMocks
ImageService service;

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
}

@Test
public void testImageDownload() {
    MutableLiveData<ImageDownloadResult> liveData = new MutableLiveData<>();
    Response<ImageResponse> response = null;
    try {
        response = service.downloadImage("test").execute();
        assertEquals(response.code(), 200);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

}

Please educate me how to inject service into the test so that I can call Http calls there. Thank you!


Answer:

I think you're confusing some bits here. In the test you seem to be testing the service, rather than the repository. The service needs to be mocked in order to actually return something and not null.

Let's start with the test setup. You'll have to declare the mocks and the thing you're testing

public class MainRepositoryTest {

  @Rule
  public MockitoRule mockitoRule = MockitoJUnit.rule();
  @Mock
  ImageService service;
  @InjectMocks
  MainRepository repository;

   //...
 }

The repository is what you actually will be testing here. As it is, you just get an instance of the repository with the service mock injected. However, this mock will return null on every method call.

So now, you'll need to set it up for each case you want to test. Say you want to test a successful response:

@Test
public void testSuccess (){
    when(service.downloadImage("test")).theReturn(<successful response>);

     repository.download("test", <mutable live data>);

     // Now assert something about the live data
}

You'll have to mock the service to return a successful response when called with the expected arguments. I'm not sure how to use live data here, because I never used it. I actually thought retrofit supported this already and that you wouldn't have to manually convert it.

As for dagger, it's great that you're using dependency injection. However, in tests usually you don't inject dependencies with dagger. Using the mockito annotation is enough here.

Question:

I'm working on a team project which has a very coupled code. I'm trying to increase the unit test coverage and I've faced the following scenario:

class Foo {
    static void methodA () {
        Bar b = new Bar();
        b.getContent();
    }
}

class Bar {

    @Inject
    DBAcessor mDBAcessor;

    Bar () {
        Dagger2.getInjector().inject(this);
    }

    public Object getContent() {
        mDBAcessor.fetchData();
    }

}

I want to unit test methodA(), however I don't know if it is possible to do it without passing the DBAcessor object via constructor to Bar() class. Bar should be a POJO model being widely used throughout the project, hence, I don't know if it would be a good idea to change its constructor and impact so many other classes. Any tips on how should I approach this scenario?


Answer:

That is wrong design.

Whenever you have an access to the constructor, pass class dependencies into constructor. That really eases unit testing. Class Bar should look:

class Bar() {

    @Inject DBAcessor mDBAcessor;

    Bar (DBAcessor dbAcessor) {
        this.mDBAcessor = dbAcessor;
    }

    public Object getContent() {
        mDBAcessor.fetchData();
    }
}

Dagger2 methods declared in Component should only be used inside object you don't have an access to the constructor (Activity or Fragment).

Then nothing prevents you from doing that:

DBAccessor dbAccessor = Mockito.mock(DBAccessor.class);
Bar bar = new Bar(dbAccessor);
bar.getContent();
verify(bar, times(1)).fetchData();

If you really need to stick to such design, then you can't make unit tests on such object, as Dagger2 depends on Application object, which needs Context, which you cannot emulate in unit tests. Write your test as androidTest, create appropriate Activity, fetch Bar object from it and test it.