2

My Utils class has an init method dependent upon external resources (e.g., a database connection) that I am unsuccessfully mocking with Mockito. Utils seems like it can be share across instance of my application, so is declared as a static (i.e., a class variable) in

public class EmailNotificationWorkItemHandler extends AbstractLogOrThrowWorkItemHandler { 
   private static Utils utils = new Utils();
  
   public void executeWorkItem(WorkItem workItem, WorkItemManager manager) {
 
   // Throw an error due to many missing parameters on the workitem
   String id= (String) workItem.getParameter("ID");

...
     try { 
         RequiredParameterValidator.validate(this.getClass(), workItem);
...
     } catch (Throwable e) {
         utils.insertErrors(id, errorCode, errorMessage, e.getStackTrace(), -1L);  // testing this method called
      }    
    
 ...
      }
  

I realize I could use dependency injection for Utils but am avoiding Spring. Postings about mocking static fields make me feel like I am close:

@Test
public void executeWorkItemMissingParametersTest() {
      
      Utils mockUtils = mock(Utils.class);
      WorkItemManager mockWorkItemMgr = mock(WorkItemManager.class);
      EmailNotificationWorkItemHandler mockWorkItemHandler =  mock(EmailNotificationWorkItemHandler.class);
      
      doAnswer(new Answer<Void>() {
          public Void answer(InvocationOnMock invocation) {
                 Object[] args = invocation.getArguments();
                 System.out.println("called with arguments: " + Arrays.toString(args));
                 return null;
            }         
      }).when(mockUtils).insertErrors(any(), any(), any(), any(), anyLong());
      
      
      try {
        doAnswer(new Answer<Void>() {          // Unfinished stubbing detected
              public Void answer(InvocationOnMock invocation) {
                     return null;
                }         
          }).when(mockUtils);
          Utils.init();
      } catch (Exception e) {
        System.out.println("In mocking of Utils.init() " + e.getLocalizedMessage());
      }
      
      WorkItemImpl workItem = new WorkItemImpl();
      workItem.setParameter("ID", "1111-AAAA");
      // Lots of required parameters removed to cause an exception and insertErrors to be called
      
      mockWorkItemHandler.executeWorkItem(workItem, mockWorkItemMgr);
      verify(mockUtils).insertErrors(any(), any(), contains("RequiredParameterValidator"), any(), anyLong());
   }

But a real instance of Utils is being used and called in mockWorkItemHandler - not the mocked Utils - and an "Unfinished stubbing detected" exception occurs where labeled above. My goal is to test the calling of utils.insertErrors as commented in the code and to do this without Utils' side effects. What am I missing in my mocks to (1) use a mocked Utils (without side effects, such as database connections) and (2) to test that mockWorkItemHandler's utils.insertErrors is called to record the missing parameters?

Please note that I have shown what seem to be all the relevant parts of EmailNotificationWorkItemHandler and Utils.

Dr Dave
  • 550
  • 1
  • 6
  • 22

1 Answers1

1

Here is a simple example of mocking construction calls using mockito inline. In order to enable mockito-inline you can refer to this answer since it is turned off by default by mockito. You can use this in your use case and it should fix your problem.

Consumer Class

public class ConsumerClass {

    Logger logger = LoggerFactory.getLogger(ConsumerClass.class);

    private static final Utils utils = new Utils();

    public String callUtilMethod(){
        logger.info("ConsumerClass callUtilMethod");
        return utils.helloWorld();
    }

}

Utils

public class Utils {

    Logger logger = LoggerFactory.getLogger(Utils.class);

    public String helloWorld() {
        logger.info("Utils class Hello World");
        return "This is from Utils";
    }
}

Testcase

 @Test
    void testConsumerClass() {
        try (MockedConstruction<Utils> mocked = Mockito.mockConstruction(Utils.class, (mock, context) -> {
            Mockito.when(mock.helloWorld()).thenReturn("This is from Mock");
        })) {
            ConsumerClass consumerClass = new ConsumerClass();
            Assertions.assertEquals("This is from Mock", consumerClass.callUtilMethod());
        }
    }
Parth Manaktala
  • 1,112
  • 9
  • 27
  • Believe he would need to place that initialisation in a BeforeClass/BeforeAll (for JUnit 4/5) as the mockConstruction() would need to hook in to the class under test before the static field constructor is invoked. – fakataha Aug 24 '22 at 03:49