Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use InjectMocks if the instance to be injected has a final class

I want to test some services with mockito. The services are based on CDI and, unfortunately, use field injection, which I can't change.

public class Service {
   @Inject Logger logger;

   public void method() {
      logger.info("some log text");
  }
}

Now it is quite easy to create the testable instances with mockito's @InjectMocks annotation. It will inject either mocks and spies.

@RunWith(MockitoJUnitRunner.class)
public class ServiceTest {
  @Spy Logger logger = LoggerFactory.getLogger(Service.class);

  @InjectMocks Service service;

  @Test public void test() {
     // Given
     // When
     service.method();
     // Then
  }

I need some working logger injected into my service class under test. The logger framework is slf4j, the preferred logger is logback. But unfortunately again, logback's implementation of Logger is final, so I can't spy on that, it results in a runtime exception.

Workarounds, that come to my mind:

  • use reflection to initialize the logger attribute on the service (poor man's injection)
  • implement a spyable (non-final) logger wrapper that delegates to the final logback logger

But is there a clean or at least better solution for that (those) problem(s)?

like image 662
Andreas Dolk Avatar asked May 21 '14 12:05

Andreas Dolk


People also ask

What does InjectMocks annotation do?

@InjectMocks is the Mockito Annotation. It allows you to mark a field on which an injection is to be performed. Injection allows you to, Enable shorthand mock and spy injections.

How do you mock injected objects?

Mockito @InjectMocks annotations allow us to inject mocked dependencies in the annotated class mocked object. This is useful when we have external dependencies in the class we want to mock. We can specify the mock objects to be injected using @Mock or @Spy annotations.

Does InjectMocks inject spy?

Again, note that @InjectMocks will only inject mocks/spies created using the @Spy or @Mock annotation. MockitoAnnotations. initMocks(this) method has to be called to initialize annotated objects. In above example, initMocks() is called in @Before (JUnit4) method of test's base class.

Can you inject a mock into a mock?

The most widely used annotation in Mockito is @Mock. We can use @Mock to create and inject mocked instances without having to call Mockito.


1 Answers

Your class under test is most likely using Logback incorrectly. Instead of declaring the logger field as being of type ch.qos.logback.classic.Logger, it should be the interface type org.slf4j.Logger, which is implemented by the final Logback logger class.

That way, you avoid the issue since there is no final class to be mocked or injected.

like image 95
Rogério Avatar answered Sep 21 '22 15:09

Rogério