编写自定义迭代器的测试用例

时间:2018-10-13 19:13:07

标签: java unit-testing junit iterator

我创建了一个自定义迭代器,该迭代器在其构造函数中接受多个迭代器,并为使用三个迭代器构造的示例生成备用输出:

  

[a,b,c],[1,2]和[x,y,z]

迭代器应按此顺序生成元素

  

a,1,x,b,2,y,c,z

我的迭代器代码:

package alternate.iterator;

import java.util.Iterator;

 /**Alternates on the given iterators.*/
 public class ImprovedAlternateIterator<E> implements Iterator {

/**Stores the iterators which are to be alternated on.*/
private Iterator<E>[] iterators;

/**The index of iterator, which has the next element.*/
private int nextIterator = 0;

/**Initializes a new AlternatingIterator object.
 * Stores the iterators in the iterators field.
 * Finds the first iterator with an available element.*/
public ImprovedAlternateIterator(Iterator<E> ... iterators) {
    this.iterators = iterators;

    if (!iterators[0].hasNext())
        findNextIterator();
}

@Override
public boolean hasNext() {

    return iterators[nextIterator].hasNext();
}

@Override
public Object next() {
    E element = iterators[nextIterator].next();

    findNextIterator();

    return element;
}

/**Steps on iterators, until one has next element.
 * It does not step on them infinitely, stops when
 * the lastly used iterator is reached.*/
private void findNextIterator() {
    int currentIterator = nextIterator;

    // Finding iterator with element remaining.
    do {
        stepNextIterator();
    } while (!iterators[nextIterator].hasNext() && nextIterator != currentIterator);
    // If it gets around to the same iterator, then there is no iterator with element.
}

/**Increases the nextIterator value without indexing out of bounds.*/
private void stepNextIterator() {
    nextIterator = (nextIterator + 1) % iterators.length;
}
}

主类:

package alternate.iterator;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.ListIterator;

public class MainApplication {

public static <E> void main(String[] args) {

    ArrayList<String> list1 = new ArrayList<String>();
    list1.addAll(Arrays.asList( "A", "B","C"));

    ArrayList<String> list2 = new ArrayList<String>();
    list2.addAll(Arrays.asList( "x", "y","z"));

    ArrayList<Integer> list3 = new ArrayList<Integer>();
    list3.addAll(Arrays.asList(1, 2));

    // ListIterator to traverse the list
    ListIterator iterator1 = list1.listIterator();
    ListIterator iterator2 = list2.listIterator();
    ListIterator iterator3 = list3.listIterator();

    ImprovedAlternateIterator <E> l = new ImprovedAlternateIterator<E>(iterator1,iterator2,iterator3);

    while (l.hasNext()) {
       System.out.print(l.next()+ " ");
    }
}
}

如何编写测试用例以测试自定义迭代器的hasNext()和next()的功能?

我真的很困惑,不知道要测试什么以及如何编写测试用例。我想了解更多有关测试的信息,如果您能通过编写示例测试用例向我展示,那对我真的很有帮助。

3 个答案:

答案 0 :(得分:1)

我将使用“测试优先”(或“测试驱动开发”(TDD))方法。将您的代码放在一边。

写出最短的编译内容(无警告-您遗漏了Iterator的type参数,并且返回的next类型是错误的):

package alternate.iterator;

import java.util.*;

/**Alternates on the given iterators.*/
public class ImprovedAlternateIterator<E> implements Iterator<E> {
    /**Initializes a new AlternatingIterator object.
     * Stores the iterators in the iterators field.
     * Finds the first iterator with an available element.*/
    public ImprovedAlternateIterator(Iterator<E> ... iterators) {
    }

    @Override
    public boolean hasNext() {
        return true;
    }

    @Override
    public E next() {
        return null;
    }
}

现在编写您最简单的测试。以最简单的方式使代码通过。必要时进行重构。再次检查测试。重复直到完成。

您可能会注意到您的界面不完整,例如,构造函数参数应为Iterator<? extends E>-无需先进行测试就无需修复。您缺少remove(很遗憾,它已成为默认方法)。

这不是通常会首先使用的方法,但是我认为这在这里非常有用。

答案 1 :(得分:0)

以下示例提供了两个使用junit和/或hamcrest的测试案例示例。 Hamcrest有很多好处,但是本质上增加了代码的可读性。值得继续阅读。

从您不想使用的断言中删除断言。

import java.util.List;

import org.junit.Test;

import com.nucleusfinancial.ImprovedAlternateIterator;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;

import static org.junit.Assert.assertEquals;

public class ImprovedAlternateIteratorTest {

    @Test
    public void hasNext_returns_next_from_first_iterator() {
        ImprovedAlternateIterator iterator = new ImprovedAlternateIterator(List.of("foo").iterator());
        // junit
        assertEquals(iterator.next(), "foo");
        // hamcrest
        assertThat(iterator.next(), is("foo"));
    }

    @Test
    public void hasNext_returns_next_from_second_iterator_after_first_iterator_has_been_iterated_over() {
        ImprovedAlternateIterator iterator = new ImprovedAlternateIterator(List.of("foo").iterator(), List.of("bar").iterator());
        //junit
        assertEquals(iterator.next(), "foo");
        assertEquals(iterator.next(), "bar");
        //hamcrest
        assertThat(iterator.next(), is("foo"));
        assertThat(iterator.next(), is("bar"));
    }
}

关于测试用例,从好的用例开始-它能达到预期的目的吗?考虑边界条件。如果输入的内容太大,太小,为空,为空或为空等,会发生什么情况。确保测试所有分支-代码覆盖工具对此很有用。

答案 2 :(得分:0)

在测试Iterator实现之前,您必须考虑什么是类的公共API 。并涵盖此API的合同。

在您的情况下,您有2种公共方法:hasNext()next()

我想补充一点,对于任何迭代器实现,hasNext()应该是幂等的。即使调用1、10或100次,结果也应该相同。

此外,您还必须考虑如何管理测试课程。什么是测试数据,如何从不同的测试用例中访问数据,以及如何在执行测试后释放资源等?

请记住,Junit的执行没有严格的测试执行顺序。
它可以调用

之类的测试

test1()test2()test3()

test2()test1() ...

因此,您无法检查next():在一种方法中,将检索所有迭代器中的第一个元素,而对于另一种方法中的第二个元素,将进行相同的操作。

以下是一些用于测试迭代器实现的框架示例:

private ImprovedAlternateIterator<Iterator> improvedIterator;

@Before
public void setUp() throws Exception {
    ArrayList<String> list1 = Lists.newArrayList("A", "B", "C");
    ArrayList<String> list2 = Lists.newArrayList("x", "y", "z");
    ArrayList<Integer> list3 = Lists.newArrayList(1, 2);

    ListIterator iterator1 = list1.listIterator();
    ListIterator iterator2 = list2.listIterator();
    ListIterator iterator3 = list3.listIterator();

    improvedIterator = new ImprovedAlternateIterator<Iterator>(iterator1, iterator2, iterator3);
}

@After
public void tearDown() throws Exception {
    improvedIterator = null;
}

/**
 * check that hasNext() is idempotent -> even if you call it 1 or 10 times the result should be the same
 */
@Test
public void testHasNextForAlternateIterator() {
    for (int i = 0; i < 20; i++) {
        assertTrue(improvedIterator.hasNext());
    }
}

/**
 * check that next() for iterator
 * it should return first element per each iterator
 */
@Test
public void testNextForAlternateIterator() {
    String expectedFromFirstIterator = "A";
    String expectedFromSecondIterator = "x";
    int expectedFromThirdIterator = 1;

    assertEquals(expectedFromFirstIterator, improvedIterator.next());
    assertEquals(expectedFromSecondIterator, improvedIterator.next());
    assertEquals(expectedFromThirdIterator, improvedIterator.next());

    String expected2FromFirstIterator = "B";
    String expected2FromSecondIterator = "y";
    int expected2FromThirdIterator = 2;

    assertEquals(expected2FromFirstIterator, improvedIterator.next());
    assertEquals(expected2FromSecondIterator, improvedIterator.next());
    assertEquals(expected2FromThirdIterator, improvedIterator.next());

    // you can omit following section if you don't need to cover it
    String expected3FromFirstIterator = "C";
    String expected3FromSecondIterator = "z";

    assertEquals(expected3FromFirstIterator, improvedIterator.next());
    assertEquals(expected3FromSecondIterator, improvedIterator.next());
}

如果测试失败,添加一些有用的信息是非常有用的:

  

assertEquals(“未检索到第一个迭代器的第一个元素”,应该是实际的);

此外,当从迭代器中检索所有元素或该元素为空时,请考虑否定情况。究竟应退还什么?一些异常或默认值。您也可以添加用于覆盖它们的案例。