Mocking a List and attempting to iterate over it

后端 未结 3 945
一生所求
一生所求 2021-01-21 02:25

Currently using Mockito to Test a method from one of my classes. My class contains a List, and the method takes in an object of the same class. The issue is when I attempt to it

相关标签:
3条回答
  • 2021-01-21 02:49

    There are some fundamental issues why your example not does work and throws a NullPointerException.

    1. The call to add() on the mocked list effectively doesn't do anything. All void methods on mocks are "no-ops" by default
    2. Iterating over a list using for-each syntax calls Collection.iterator() under the hood. This returns null, because you've not setup mockito to return anything else.

    Instead, I would not mock the list and instead pass an actual list. Arrays.asList() is convenient for testing.

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

    If you're determined to mock a list then you'll have to mock its behaviour, i.e. making the add() actually store something and .iterator() return an iterator. This can be done rather painfully as follows. I've only included this to demonstrate the principle.

    @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);
        }
    }
    
    0 讨论(0)
  • 2021-01-21 02:53

    As @Adam says: "Iterating over a list using for-each syntax calls Collection.iterator() under the hood. This returns null because you've not setup mockito to return anything else." So you have to setup mockito in this way;

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

    This way is verbose, but you are free to modify the behaviour of the array list and also understand how it works under the wood.

    0 讨论(0)
  • 2021-01-21 02:55

    You can't add values to the Mocked element. you can remove @Mock from the list of data and use new keyword to initilize it.

    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());
    }
    
    0 讨论(0)
提交回复
热议问题