Thursday 25 October 2012

Mockito - modifying parameters

My previous post was about how to get Mockito to generate a new instance of a class each time it is called.

Another difficult problem when testing using mocks is how to mock function calls which modify the parameters passed in. Turns out you can actually do this using nearly the same technique. The InvocationOnMock parameter passed in can be interrogated to find the parameters passed in and then the parameters can be altered.

My case was even worse as I needed the parameter to a void function to be modified but it turns out the same technique works.

To do this you use the doAnswer() function but you use it with a void

doAnswer( new Answer<Void>) ...

What I was trying to do was to test a function which called a JpaRepository<>.save() method and then returned the auto-generated ID from within the modifed entity.

So the example looks like this:


doAnswer(new Answer<Void>() 
{
    @Override
    public Void answer(InvocationOnMock invocation) 
      throws Throwable 
    {
        Object[] arguments = invocation.getArguments();

        if (    arguments != null 
                && 
                arguments.length > 0 
                && 
                arguments[0] != null
        {
            SomeEntity entity = (SomeEntity) arguments[0];
            entity.setId(testInternalId);
        }
        return null;
    }     
}).when(mockRepository).save(any(SomeEntity.class));


Friday 5 October 2012

Mockito stubs

So I was testing this bit of code that is time sensitive.

To test all of the conditions I needed to be able to simulate time passing in my test. The obvious thing to do was to create a facade for fetching the calendar (wrapping Calendar.getInstance()) and then mocking this in my test. Then my mock could return whatever time I wanted.

The problem was that the mock was called more than once and always returned the *same* calendar instance. The code would then manipulate this calendar to do time calculations but because each call returned the same instance this had undesirable effects.

The solution was to use the Mockito stubbing API with the mock. You can create a callback that is called each time the mock interface is called and you can have it do whatever you want. I simple set it up to return a new instance each time

    @Mock CalendarFacade        mockCalendarFacade;

    private void setupCalendarWithTimeInPastMock(final int minutesAgo)
    {
        stub(mockCalendarFacade.getCalendar()).toAnswer(new Answer()
        {
            public Calendar answer(InvocationOnMock invocation)
            {
                return calendarMinutesAgo(minutesAgo);
            }
        });
    }

    private Calendar calendarMinutesAgo(Integer minutes)
    {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE,-minutes);
        return calendar;
    }