5

I have a class I need to test that has two instance of different class but same interface. This is how the codes looks like,

Class to be tested:

@Service
public class MainClass {
   @Resource(name = "aClass")
   private IClass instance1;
   @Resource(name = "bClass")
   private IClass instance2;
}

Other classes:

@Service("aClass")
public class A implements IClass {}

@Service("bClass")
public class B implements IClass {}

My Unit Test:

public MainClassTest {
   @InjectMocks
   private MainClass mainClass;

   @Mock
   private IClass instance1;
   @Mock
   private IClass instance2;

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

When I run the test, both instance1 and instance2 are null since they are not mocked. This doesn't happen when the interface has only one implementation.

Any idea how to handle this?

Thanks, Angelo

Demian
  • 390
  • 3
  • 8
  • 15

2 Answers2

6

@InjectMocks is a short cut which tells Mockito to instance your MainClass and to try to inject mocks by one of constructor injection or setter injection or property injection (in order, IIRC).

I think the behaviour you are seeing may be known behaviour for Mockito; there's an open issue describe here realting to Mockito's handling of @InjectMocks with two instances of the same mocked type.

So, instead of relying on it, you could create the instance of MainClass the 'old fashioned' way e.g. in a @Before method. For example:

private MainClass mainClass;

@Before
public void setup() {
    IClass instance1 = mock(IClass.class); 
    IClass instance2 = mock(IClass.class);

    mainClass = new MainClass(instance1, instance2);
}
glytching
  • 44,936
  • 9
  • 114
  • 120
  • I would recommend always doing this anyway. Injecting private fields with no setter or constructor injection gets messy real fast. – Christopher Schneider Sep 18 '17 at 16:26
  • It is one of those things which is useful when it works but painful when it goes wrong. For my part, I'm not yet convinced about switching to using `@InjectMocks`. – glytching Sep 18 '17 at 16:28
  • Yeah, it's often a sign of poor design. I almost wish Spring didn't allow field level injection. I once was on a large project with 100% field level injection, and it seemed to always lead to adding just one more field. Eventually, there ended up being classes with 10+ dependencies, with the worst being around 20. I think constructor injection forces you to to keep your dependencies manageable. A constructor with 20 args would just look wrong :-) – Christopher Schneider Sep 18 '17 at 16:38
  • "Just one more field" ... what's the worst that could happen ;) – glytching Sep 18 '17 at 16:46
1

Try using instance1 = mock(aClass.class); and instance2 = mock(bClass.class);.