4

I have below setup of classes.

class Base {
   @Autowired
   private BaseService service; //No getters & setters
   ....
}

@Component
class Child extends Base {
  private final SomeOtherService otherService;

  @Autowired   
  Child(SomeOtherService otherService) {
     this.otherService = otherService;
  }
}

I am writing a unit test for the Child class. If I use @InjectMocks then the otherService comes out to be null. If I use, the constructor of Child class in the setup of the test, then the fields in Base class comes out to be null.

I know all the arguments about field injection being evil, but I am more interested in knowing if there is a way to solve this without changing the way Base and Child classes injects their properties?

Thanks!!

Mubin
  • 4,192
  • 3
  • 26
  • 45
  • Yes, don't use Spring to do the injection. Create the mocks and inject them in your test setup however you wish. – duffymo Mar 27 '17 at 11:38
  • Can you please explain in detail? I can create the mock but cannot inject them. If I use `@InjectMocks` to inject the mocks, then the properties of `Child` class are `null` as they use constructor injection. I cannot use the constructor to initiatilize the class under test, as I have no way of passing the properties of `Base` class from the test. – Mubin Mar 27 '17 at 11:41
  • Include your testcase. – M. Deinum Mar 27 '17 at 11:52
  • You can inject them. Call the constructor or setter for your object and pass in the mock. The base object has to be initialized to use properly. Inject in what's needed. Leave Spring out of it. – duffymo Mar 27 '17 at 11:52
  • How does base class get its service? Both the base and child have to initialize it. Sounds like your issue is not testing or mocking, but bad class implementation. – duffymo Mar 27 '17 at 11:58

1 Answers1

6

Just do this:

public class Test {
    // Create a mock early on, so we can use it for the constructor:
    OtherService otherService = Mockito.mock(OtherService.class);

    // A mock for base service, mockito can create this:
    @Mock BaseService baseService;

    // Create the Child class ourselves with the mock, and
    // the combination of @InjectMocks and @Spy tells mockito to
    // inject the result, but not create it itself.
    @InjectMocks @Spy Child child = new Child(otherService);

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

Mockito should do the right thing.

john16384
  • 7,800
  • 2
  • 30
  • 44
  • 1
    `the combination of @InjectMocks and @Spy tells mockito to inject the result, but not create it itself.` - This was the key. Thanks a ton!! – Mubin Mar 27 '17 at 20:11