14
votes

I need to test handleIn() method using Mockito.

However the code need to call this legacy code Util.getContextPDO which is a static method.

Note that in testing environment this Util.getContextPDO is always returns Exception, and I intend to bypass this Util.getContextPDO() by always return a dummy IPDO.

public class MyClass {
  public IPDO getIPDO() 
  {
    return Util.getContextPDO(); // note that Util.getContextPDO() is a static, not mockable.
  }

  public String handleIn(Object input) throws Throwable 
  {
    String result = "";
    IPDO pdo = getIPDO();

    // some important business logic.

    return result;
  } 
}

Initially I thought this achieveable by using spy() of the class "MyClass", so I can mock the return value of getIPDO(). Below is my initial effort using spy ()

@Test
public void testHandleIn() throws Exception
{
    IPDO pdo = new PDODummy();


    MyClass handler = new MyClass ();
    MyClass handler2 = spy(handler);

    when(handler2.getIPDO()).thenReturn(pdo);
    PDOUtil.setPDO(pdo, LogicalFieldEnum.P_TX_CTGY, "test123");
    IPDO pdoNew = handler2.getIPDO();

    Assert.assertEquals("test123,(PDOUtil.getValueAsString(pdoNew, LogicalFieldEnum.P_TX_CTGY)));

}

However the when(handler2.getIPDO()).thenReturn(pdo); is throwing the Exception that I want to avoid ( because handler2.getIPDO() ) seems to call the real method.

Any idea on how to test this part of code?

3
We are using PowerMock together with Mockito to wotk with legacy. This turns so easy so we made rule to avoid PowerMock to fight with legacy more effective - Eugen Martynov
The "only" issue with PowerMock is that you are forced to use its own test runner, which is not always an option (when writing Android Robolectric tests, for instance, which require their own robolectric runner) - David Santiago TuriƱo

3 Answers

12
votes

Changed my testing to :

@Test
public void testHandleIn() throws Exception
{
  IPDO pdo = new PDODummy();


  MyClass handler = new MyClass ();
  MyClass handler2 = spy(handler);

  doReturn(pdo ).when( handler2 ).getIPDO();
  PDOUtil.setPDO(pdo, LogicalFieldEnum.P_TX_CTGY, "test123");
  IPDO pdoNew = handler2.getIPDO();

  Assert.assertEquals("test123,(PDOUtil.getValueAsString(pdoNew, LogicalFieldEnum.P_TX_CTGY)));

}

Solved after reading Effective Mockito.

12
votes

A good technique for getting rid of static calls on 3rd party API is hiding the static call behind an interface.

Let's say you make this interface :

interface IPDOFacade {

    IPDO getContextPDO();
}

and have a default implementation that simply calls the static method on the 3rd party API :

class IPDOFacadeImpl implements IPDOFacade {

    @Override
    public IPDO getContextPDO() {
        return Util.getContextPDO();
    }
}

Then it is simply a matter of injecting a dependency on the interface into MyClass and using the interface, rather than the 3rd party API directly :

public class MyClass {

    private final IPDOFacade ipdoFacade;

    public MyClass(IPDOFacade ipdoFacade) {
        this.ipdoFacade = ipdoFacade;
    }

    public String handleIn(Object input) throws Throwable
    {
        String result = "";
        IPDO pdo = getIPDO();

        someImportantBusinessLogic(pdo);

        return result;
    }

    ...

}

In your unit test, you can then easily mock your own interface, stub it any way you like and inject it into the unit under test.

This

  • avoids the need to make private methods package private.
  • makes your tests more readable by avoiding partial mocking.
  • applies inversion of control.
  • decouples your application from a specific 3rd party library.
1
votes
when(handler2.getIPDO()).thenReturn(pdo);

Will actually call the method and then return pdo regardless.

Whereas:

doReturn(pdo).when(handler2).getIPDO();

Will return pdo without calling the getIPDO() method.