5

I have this Service Interface

public interface ABC {
    public String getAbc();
}

And I have this controller that uses this Service

public class Do {
    @Inject ABC abc;

    public String doAbc() {
       String a = abc.getAbc();
    }
}

In my Unit test, I want to be able to mock out this ABC and Not have to provide an actual implementation. Here is what works:

public class TestDo {
   @Mock
   private Do do;

   @Mock
   private ABC abc;

   @Before
   public void init() {
       MockitoAnnotations.initMocks(this);
       do.abc = abc;
   }

   @Test
   public void testDo() {
       when(do.doAbc()).thenCallRealMethod();
       do.doAbc();
       assertSomething...
   }
}

But what I do not want to do is do.abc = abc;, from all my searches, in order to do @InjectMock, I need to provide a concrete class. Is there a way for me to use the interface but yet able to Inject the mocked interface into do without the need to exposed my ABC inject in Do Class? I want to make ABC a private variable, and still able to inject an interface through Mockito.

Churk
  • 4,556
  • 5
  • 22
  • 37

2 Answers2

5
public interface ABC {
    public String getAbc();
}

public class Do {

    @Inject
    ABC abc;

    public String doAbc() {
        System.out.println("Injected abc is " + (abc == null ? "null" : "not null"));
        return abc.getAbc();
    }

}


public class TestDo {
    @Mock
    private ABC abc;

    @InjectMocks
    private Do ddo;

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

    @Test
    public void simpleTest() {
        System.out.println(ddo.doAbc());
    }

}

output is:

Injected abc is not null
null
Rustam
  • 1,397
  • 1
  • 13
  • 17
  • @Churk, Tested it with Mockito 1.9.5, JDK 1.8. All works. I will update my answer to full working code. – Rustam Feb 11 '16 at 08:13
  • I re-read your code. My mistake was I had the /@InjectMocks on what is equivalent to ABC on my actual code, therefore, it was trying to create an instance of an interface that was throwing me off. And had /@Mock on whats equivalent to Do, so my mocking and injectMocking was backward. – Churk Feb 16 '16 at 13:23
4

The example you gave is a perfect case for constructor injection: You can make your dependency private (and even final, which is always preferable when practical) and still build an instance without needing an injection framework:

public class Do {
    private final ABC abc;

    @Inject
    public Do(final ABC abc) {
        this.abc = abc;
    }

    public String doAbc() {
       String a = abc.getAbc();
    }
}
chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
  • I do not want to expose ABC all together. I want to do ```@Inject private ABC abc;``` – Churk Feb 11 '16 at 02:38
  • 2
    @Churk Your `ABC` is a dependency. Your `Do` class *must* have it. Using field injection to hide the dependency simply makes it more likely that something will fall apart later on--in addition to making testing more difficult. The entire point of DI is to remove magic dependencies by making them explicit. – chrylis -cautiouslyoptimistic- Feb 11 '16 at 03:06
  • It is not to hid the dependencies, but in my real usage, it is a DataAccessService, I do not want anyone able to have direct access to this Service. Thats why I want it to be private. – Churk Feb 11 '16 at 14:20
  • 1
    @Churk It sounds like you don't understand what Java access modifiers are for. The DI system *has* to have access to the service to inject it no matter what, so you're not exposing anything extra by using constructor injection. – chrylis -cautiouslyoptimistic- Feb 11 '16 at 14:39
  • 1
    correct me if I am wrong, your constructor allows this ```Do do = new Do(myABC);```, now security stand point, i just tell my application to allow who ever is constructing this service to inject their own ABC class. Vs ```@Inject private ABC abc``` and my constructor does not take in any ABC class forcing the injection done only at spring level. So do I understand java access or do i need to explain more? – Churk Feb 16 '16 at 13:11