使用Mockito测试Java增强的行为

使用Mockito测试Java增强的行为,java,unit-testing,for-loop,mockito,Java,Unit Testing,For Loop,Mockito,我想使用Mockito测试一个java方法,该方法上有一个增强的for。问题是,当我不知道如何设定对增强型工作的期望时。以下代码来自: 导入静态org.mockito.mockito.when; 导入静态org.testng.Assert.assertTrue; 导入java.util.ArrayList; 导入java.util.Iterator; 导入java.util.List; 导入org.mockito.mockito; 导入org.testng.annotations.Test; 公

我想使用Mockito测试一个java方法,该方法上有一个增强的for。问题是,当我不知道如何设定对增强型工作的期望时。以下代码来自:

导入静态org.mockito.mockito.when;
导入静态org.testng.Assert.assertTrue;
导入java.util.ArrayList;
导入java.util.Iterator;
导入java.util.List;
导入org.mockito.mockito;
导入org.testng.annotations.Test;
公共类列表测试
{
@试验
公开无效测试()
{
List mockList=Mockito.mock(List.class);
迭代器mockIterator=Mockito.mock(迭代器.class);
when(mockList.iterator()).thenReturn(mockIter);
when(mokiter.hasNext()).thenReturn(true).thenReturn(false);
when(mokiter.next())。然后返回(“A”);
布尔标志=假;
用于(字符串s:模拟列表){
flag=true;
}
资产真实性(标志);
}
} 
for循环中的代码永远不会执行。为迭代器设置期望值不起作用,因为java增强版for在内部不使用列表迭代器。设置
List.get()
方法的期望值也不起作用,因为增强型for实现似乎也没有调用List的
get()
方法


任何帮助都将不胜感激。

除非我遗漏了什么,否则您可能应该返回一个真实的模拟值列表。在本例中,在生成器方法中构造测试字符串列表,并简单地返回该列表。在更复杂的情况下,可以使用模拟对象替换列表的内容


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

模仿迭代器对我很有用。请参见下面的代码示例:

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);
    }
}
导入静态org.junit.Assert.assertEquals;
导入静态org.mockito.mockito.mock;
导入静态org.mockito.mockito.when;
导入java.util.Collection;
导入java.util.Iterator;
导入org.junit.Before;
导入org.junit.Test;
公共类TestMockEditor{
私人收藏水果;
私有迭代器;
@抑制警告(“未选中”)
@以前
公共作废设置(){
水果迭代器=mock(迭代器类);
when(fruitIterator.hasNext())。然后返回(true、true、true、false);
when(fruitIterator.next()).thenReturn(“苹果”)
然后返回(“香蕉”)。然后返回(“梨”);
水果=模拟(Collection.class);
when(fruits.iterator())。然后返回(fruitIterator);
}
@试验
公开无效测试(){
int迭代次数=0;
用于(串水果:水果){
迭代++;
}
资产质量(3,迭代);
}
}

我只想指出一点,因为我整天都在为此而挣扎:

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


你想做这样的事

/**
    * 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();

    }
/**
*你想让它变得易懂的模拟
*/
@嘲弄
javax.inject.Instance myMockedInstanceObject;
/**
*将myMockedInstanceObject mock设置为在业务逻辑
*要循环iterable上的现有实例。。。。
*/
私有void setupTransportOrderToTransportEquipmentMapperInstancesToBeiteratable()的设置{
//(a)创建一个非常真实的迭代器对象
最终迭代器迭代器=数组
.asList(Integer.valueOf(1),Integer.valueOf(2)).iterator();
//(b)确保循环时模拟返回正确的迭代器
Mockito.doAnswer(新答案(){
@凌驾
公共迭代器应答(调用锁调用)
扔掉的{
返回迭代器;
}
}).when(myMockedInstanceObject).iterator();
}

coments和javadoc行应该清楚地说明如何模拟任何iterable的行为,而不管它是一个列表、一个集合、一个javax.inject.instance或其他任何东西。

。我知道它可以通过创建模拟列表进行测试。我只是想知道增强的for循环在做什么,因为它在任何时候都不使用迭代器或调用列表的get()方法。那么它如何访问数据结构呢?有意思吧?我也试过执行你的代码。一旦我将'mockIterator'变量重命名为'mockIter',测试就通过了。在你的例子中,你知道项目的确切数量,因此你可以模拟'hasNext()`,如果你在for循环中创建这些项目,而你事先不知道对象的数量呢?以上问题有答案吗?如果我们不知道迭代次数呢?如果你不知道迭代次数(在单元测试中有点奇怪),那么这项技术就不能应用。
anyObject()
现在不推荐使用,你可以使用:
doCallRealMethod().when(myMockedList).forEach(any())
doCallRealMethod().when(myMockedList).forEach(anyObject());
/**
    * 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();

    }