使用 Mockito 测试增强的 Java 行为

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6379308/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 05:27:55  来源:igfitidea点击:

Testing Java enhanced for behavior with Mockito

javaunit-testingfor-loopmockito

提问by HackerGil

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:

我想使用 Mockito 测试一个具有增强功能的 java 方法。问题是,当我不知道如何为增强的工作设置期望时。以下代码来自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(mockIter.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.

for 循环中的代码永远不会被执行。为迭代器设置期望值不起作用,因为增强的 java 在内部不使用列表迭代器。设置List.get()方法的期望也不会,因为增强的实现似乎也不会调用get()列表的方法。

Any help will be much appreciated.

任何帮助都感激不尽。

采纳答案by hoipolloi

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);
    }
}

回答by Andrew White

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.

最后,我无法想象为什么您真的需要模拟增强的 for 循环。单元测试的性质不适合这种级别的检查。这仍然是一个有趣的问题。

回答by OneWholeBurrito

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):

如果要使用myList.forEach(...)语法而不是for(:),则必须包括(在其中设置模拟列表的位置):

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

回答by 99Sono

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.

行注释和 javadoc 应该足够清楚如何模拟任何可迭代对象的行为,无论它是列表、集合还是 javax.inject.instance 或其他任何东西。