开发者

Testing Java enhanced for behavior with Mockito

开发者 https://www.devze.com 2023-03-13 18:35 出处:网络
I want to test a java method that has an enhanced for on it using Mockito. The problem is that when I don\'t know how to set the expectations for the enhanced for to work. The following code was gotte

I want to test a java method that has an enhanced for on it using Mockito. The problem is that when I don't know how to set the expectations for the enhanced for to work. The following code was gotten from an unanswered question in the mockito google group:

import static org.mockito.Mockito.when;
import static org.testng.Assert.assertTrue;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.mockito.Mockito;
import org.testng.annotations.Test;

public class ListTest
{

  @Test
  public void test()
  {
    List<String> mockList = Mockito.mock(List.class);
    Iterator<String> mockIterator = Mockito.mock(Iterator.class);

    when(mockList.iterator()).thenReturn(mockIter);
    when(mockI开发者_开发知识库ter.hasNext()).thenReturn(true).thenReturn(false);
    when(mockIter.next()).thenReturn("A");

    boolean flag = false;
    for(String s : mockList) {
        flag = true;
    }
    assertTrue(flag);
  }
} 

The code inside the for loop never gets executed. Setting expectations for an iterator doesn't work, because the java enhanced for doesn't use the list iterator internally. Setting expectations for List.get() method doesn't either since the enhanced for implementation doesn't seem to call the get() method of the list either.

Any help will be much appreciated.


Mocking the iterator works for me. See below code sample:

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.Collection;
import java.util.Iterator;

import org.junit.Before;
import org.junit.Test;

public class TestMockedIterator {

    private Collection<String> fruits;
    private Iterator<String> fruitIterator;

    @SuppressWarnings("unchecked")
    @Before
    public void setUp() {
        fruitIterator = mock(Iterator.class);
        when(fruitIterator.hasNext()).thenReturn(true, true, true, false);
            when(fruitIterator.next()).thenReturn("Apple")
            .thenReturn("Banana").thenReturn("Pear");

        fruits = mock(Collection.class);
        when(fruits.iterator()).thenReturn(fruitIterator);
    }

    @Test
    public void test() {
        int iterations = 0;
        for (String fruit : fruits) {
            iterations++;
        }
        assertEquals(3, iterations);
    }
}


Unless I am missing something, you should probably be returning a real list of mocked values. In this case, construct your list of test string in a generator method and simply return that. In more complex cases you can replace the contents of the list with mocked objects.

As a closing point, I can't imagine why you would ever really need to mock an enhanced for loop. The nature of unit tests don't lend themselves well to that level of inspection. It is an interesting question none the less.


Just want to point something out, because I struggled with this all day:

If you want to use the myList.forEach(...) syntax instead of for(:), you have to include (where you set up your mocked list):

doCallRealMethod().when(myMockedList).forEach(anyObject());


You want to do something like this.

/**
    * THe mock you want to make iterable
    */
   @Mock
   javax.inject.Instance<Integer> myMockedInstanceObject;

   /**
     * Setup the myMockedInstanceObject mock to be iterable when the business logic
     * wants to loop existing instances of the on the iterable....
     */
    private void setupTransportOrderToTransportEquipmentMapperInstancesToBeIteratble() {
        // (a) create a very real iterator object
        final Iterator<Integer> iterator = Arrays
                .asList(Integer.valueOf(1), Integer.valueOf(2)).iterator();

        // (b) make sure your mock when looped over returns a proper iterator       
        Mockito.doAnswer(new Answer<Iterator<Integer>>() {
            @Override
            public Iterator<Integer> answer(InvocationOnMock invocation)
                    throws Throwable {
                return iterator;
            }
        }).when(myMockedInstanceObject).iterator();

    }

The line coments and javadoc should make it clear enough how to mock the behavior of any iterable, regardless of it being a list, a collection a javax.inject.instance or whatever.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号