使用 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
Testing Java enhanced for behavior with Mockito
提问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 或其他任何东西。