【问题标题】:Testing Java enhanced for behavior with Mockito使用 Mockito 测试 Java 的行为增强
【发布时间】:2011-06-16 22:20:58
【问题描述】:

我想测试一个使用 Mockito 对其进行了增强的 java 方法。问题是当我不知道如何为增强的工作设定期望时。以下代码来自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(mockIter.hasNext()).thenReturn(true).thenReturn(false);
    when(mockIter.next()).thenReturn("A");

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

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

任何帮助将不胜感激。

【问题讨论】:

    标签: java unit-testing for-loop mockito


    【解决方案1】:

    模拟迭代器对我有用。请参阅下面的代码示例:

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

    【讨论】:

    • 在您的示例中,您可以从确切的项目数之前知道,因此您可以模拟`hasNext()`,如果在不知道项目数量的情况下在 for 循环中创建这些项目怎么办?提前对象?
    • 以上问题有答案吗?如果我们不知道迭代次数怎么办?
    • 如果你不知道迭代次数(这在单元测试中有点奇怪)那么这个技术就不能应用了。
    【解决方案2】:

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

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

    【讨论】:

    • 完全。我知道可以测试它创建一个模拟列表。我只是想知道增强的 for 循环在做什么,因为它既不是使用迭代器,也不是随时调用列表的 get() 方法。那么它如何访问数据结构呢?很有趣吧?
    • 另外,我尝试执行您的代码。一旦我将“mockIterator”变量重命名为“mockIter”,测试就通过了。
    【解决方案3】:

    只是想指出一些事情,因为我整天都在为此苦苦挣扎:

    如果您想使用myList.forEach(...) 语法而不是for(:),则必须包括(在哪里设置模拟列表):

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

    【讨论】:

    • anyObject() 现已弃用,您可以使用:doCallRealMethod().when(myMockedList).forEach(any());
    【解决方案4】:

    你想做这样的事情。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多