繁体   English   中英

模拟列表并尝试对其进行迭代

[英]Mocking a List and attempting to iterate over it

当前正在使用Mockito从我的一个类中测试一种方法。 我的班级包含一个List,并且该方法接受同一班级的对象。 问题是当我尝试从对象遍历List时。 我得到一个指向列表的空指针。 在下面,您将看到代码片段。

private Shipment shipment;
private Shipment shipment2;
@Mock
private Order mockOrder1;
@Mock
private Order mockOrder2;
@Mock
private Order mockOrder3;
@Mock
private ArrayList<Order> mockShipmentOrders;
@Mock
private ArrayList<Order> mockShipmentOrders2;

@Before
public void setUp(){
    MockitoAnnotations.initMocks(this);
    mockShipmentOrders.add(mockOrder1);
    mockShipmentOrders.add(mockOrder2);
    mockShipmentOrders2.add(mockOrder3);
    shipment = new Shipment(1, mockShipmentOrders);
    shipment2 = new Shipment(2, mockShipmentOrders2);
}

@Test
public void test_mergeShipments_increasesByOneWhenAShipmentOfOneAddedToAShipmentORderSizeOfTwo(){
    shipment.mergeShipments(shipment2);
    assertEquals(3, shipment.getShipmentOrders().size());
}

上面可以看到我的模拟测试,下面是我的Class和方法:

公共类货运{

private long shipmentID;
private List<Order> shipmentOrders;

public Shipment(long shipmentID, List<Order> shipmentOrders){
    this.shipmentID = shipmentID;
    this.shipmentOrders = shipmentOrders;
}

public List<Order> getShipmentOrders(){
    return shipmentOrders;
}

public void mergeShipments(Shipment shipment2){     
    List<Order> existingShipment = shipment2.getShipmentOrders();
    for (Order order : existingShipment){
        shipmentOrders.add(order);
    }
}

当我运行测试时,我在以下行中获取了java.lang.NullPointerException:for(Order order:existingShipment){in mergeShipemts();

问题是; 是否可以模拟列表,调用该列表,然后在该模拟列表上运行foreach?

为什么您的示例不起作用并抛出NullPointerException有一些基本问题。

  1. 有效调用模拟列表上的add()不会执行任何操作。 默认情况下,模拟中的所有void方法均为“ no-ops”
  2. 使用for-each语法遍历列表将在后台调用Collection.iterator() 这将返回null,因为您尚未将Mockito设置为返回其他任何内容。

相反,我不会模拟该列表,而是传递实际列表。 Arrays.asList()便于测试。

@Before
public void setUp(){
    MockitoAnnotations.initMocks(this);
    shipment = new Shipment(1, Arrays.asList(mockOrder1, mockOrder2));
    shipment2 = new Shipment(2, Arrays.asList(mockOrder3));
}

如果确定要模拟列表,则必须模拟其行为,即使add()实际上存储一些东西,而.iterator()返回一个迭代器。 如下所述,这可能非常痛苦。 我仅包括此内容以说明原理。

@Mock
private List<String> mockedList;

@Before
public void init() {
    MockitoAnnotations.initMocks(this);

    List<String> realList = new ArrayList<>();
    doAnswer(new Answer<String>() {
        @Override
        public String answer(InvocationOnMock invocation) throws Throwable {
            realList.add(invocation.getArgumentAt(0, String.class));
            return null;
        }

    }).when(mockedList).add(any());

    when(mockedList.iterator()).thenAnswer(new Answer<Iterator<String>>() {

        @Override
        public Iterator<String> answer(InvocationOnMock invocation) throws Throwable {
            return realList.iterator();
        }
    });

    mockedList.add("bar");
    mockedList.add("baz");
}

@Test
public void iterateOverMockedList() {
    for (String each : mockedList) {
        System.out.println(each);
    }
}

您不能向Mocked元素添加值。 您可以从数据列表中删除@Mock并使用new关键字对其进行初始化。

private Shipment shipment;
private Shipment shipment2;
@Mock
private Order mockOrder1;
@Mock
private Order mockOrder2;
@Mock
private Order mockOrder3;

private ArrayList<Order> mockShipmentOrders;

private ArrayList<Order> mockShipmentOrders2;

@Before
public void setUp(){
    MockitoAnnotations.initMocks(this);
    mockShipmentOrders = new ArrayList<>();
    mockShipmentOrders2 = new ArrayList<>();
    mockShipmentOrders.add(mockOrder1);
    mockShipmentOrders.add(mockOrder2);
    mockShipmentOrders2.add(mockOrder3);
    shipment = new Shipment(1, mockShipmentOrders);
    shipment2 = new Shipment(2, mockShipmentOrders2);
}

@Test
public void test_mergeShipments_increasesByOneWhenAShipmentOfOneAddedToAShipmentORderSizeOfTwo(){
    System.out.println(shipment);
    System.out.println(shipment2);
    shipment.mergeShipments(shipment2);

    assertEquals(3, shipment.getShipmentOrders().size());
}

正如@Adam所说:“使用for-each语法遍历列表将在幕后调用Collection.iterator() 。这将返回null,因为您尚未设置mockito来返回任何其他内容。” 因此,您必须以这种方式设置模仿。

    @Test
public void test_mergeShipments_increasesByOneWhenAShipmentOfOneAddedToAShipmentORderSizeOfTwo(){

      //GIVEN

   //Mock the iterator
    Iterator<Order> stockIteratorMock = mock(Iterator.class);

    //WHEN

    //In setUp method you put two objs
    when(mockShipmentOrder.size()).thenReturn(2); 

   //Set a mock for iterator
    when(mockShipmentOrder.iterator()).thenReturn(iteratorMock);

   // Instruct the iteratorMock when stop to return item
    when(iteratorMock.hasNext())
            .thenReturn(true)
            .thenReturn(true)
            .thenReturn(false);

    // Instruct the iteratorMock what obj return on each call
    // You can skip this: mockShipmentOrders.add(mockOrder1);
    when(stockIteratorMock.next())
      .thenReturn(mockOrder1)
      .thenReturn(mockOrder2);

    shipment.mergeShipments(shipment);

    //THEN
    assertEquals(2, shipment.getShipmentOrders().size());
}

这种方法很冗长,但是您可以随意修改数组列表的行为,也可以了解它在后台的工作方式。

我有类似的问题,下面是这种情况:我在void方法内有下面的方法循环:

List<Message> msgList = service1.getList();
for (Message message : msgList) {
 StorageObject object = cloudStorage.readObject(anotherObject);
 InputStream inputStream = object .getObjectContent();
 String text = IOUtils.toString(inputStream);
 // text to object mapping
 // third party service call
}

在我的单元测试用例中,我完成了以下模拟:

  1. service1.getList()返回2个消息对象的列表
  2. 模拟存储对象并为其提供一些模拟值,如下所示

    StorageObject stObject = new StorageObject(); stObject.setObjectContent(new StorageObjectInputStream(new ByteArrayInputStream(“嗨,这是一个虚拟对象,它将是json格式” .getBytes()),null)));

当执行测试用例时,对于第一次迭代,它可以完美地工作,并且方法执行正确的结果,但是对于第二次迭代,它返回空文本,为什么呢?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM