Moiz Raja
Moiz Raja

Reputation: 5760

Unit testing classes that use dagger 2 to create objects

Let's say I have a dagger 2 module as follows,

@Module
interface Creator {
    MyClass create();
}

and I am using it to create an instance of MyClass

class Instantiator {
    void doSomething(){
        MyClass clazz = DaggerCreator.create().create();
        // do things with clazz

    }
}

It seems to me that I cannot effectively test the doSomething method in Instantiator because I cannot provide a mock for MyClass.

Am I wrong? If not are we supposed to use Dagger instantiation sparingly?

Upvotes: 1

Views: 67

Answers (1)

David Rawson
David Rawson

Reputation: 21457

You are correct in saying that it is hard to test use of a Component injector, since this is a static method. But harder than what? Here is the same method using instantiation:

class Instantiator {
    void doSomething(){
        MyClass clazz = new MyClass();
        // do things with clazz

    }
}

still hard to test, right?

The point is to use as few Component (injectors) as possible and to pass in dependencies in the constructor for your objects. Dagger 2 makes resolving the dependencies in the constructor easy. This thereby makes testing easy since you can pass in mock object in a constructor.

Let's refactor the code you wrote to be testable. Assume that MyClass contains a single method, fireLazers() that you want to test is being invoked inside Instantiator's doSomething() method:

public class DoerOfSomething {

    private final MyClass myClass;

    @Inject
    public DoerOfSomething(MyClass myClazz) {
        this.myClass = myClazz;
    }

    public void doSomething() {
        myClass.fireLazers();
    }

}

Now you can write a test like this using a mock object:

public void DoerOfSomethingTest {

    //mocks
    MyClass mockMyClass;

    //system under test
    DoerOfSomething doerOfSomething;

    @Before
    public void setUp() {
        mockMyClass = Mockito.mock(MyClass.class);
    }

    @Test
    public void whenDoSomething_thenInteractsWithMyClass() {
        //arrange
        doerOfSomething = new DoerOfSomething(mockMyClass);

        //act
        doerOfSomething.doSomething();

        //assert
        verify(mockMyClass).fireLazers();
    }
}

Of course, you will now need to inject DoerOfSomething into the top level class where you are injecting, but now you can be certain that the object you are injecting is functioning as expected because it is testable. Your code for using Dagger looks a bit unusual but I'll use your idioms for the sake of parity between the question and the answer.

class Instantiator {

    private final DoerOfSomething doerOfSomething;

    Instantiator() {
         doerOfSomething = DaggerCreator.create().create();
    }

    void doSomething() {
        doerOfSomething.doSomething();
    }

}

Upvotes: 1

Related Questions