Написание тестовых случаев для пользовательского итератора - PullRequest
0 голосов
/ 13 октября 2018

Я создал собственный итератор, который принимает несколько итераторов в своем конструкторе и выдает альтернативный вывод для примера, составленного из трех итераторов:

[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 голосов
/ 14 октября 2018

Перед тестированием реализации 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 ("Первый элемент из первого итератора не получен", ожидаемый, фактический);

Также рассмотрим отрицательные случаи, когда вы извлекали все элементы из вашего итератора или он пуст.Что именно должно быть возвращено?Некоторое исключение или значение по умолчанию.Вы можете добавить случаи для их покрытия.

0 голосов
/ 14 октября 2018

Я бы воспользовался подходом Test First (или Test Driven Development (TDD)). На мгновение отложите код.

Напишите самую короткую вещь, которая компилируется (без предупреждений - вы пропускаетевведите аргумент в Iterator, а возвращаемый тип 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 (он сталметод по умолчанию, к сожалению).

Это не подход Я обычно использовал бы сначала, но я думаю, что это очень полезно здесь.

0 голосов
/ 13 октября 2018

В следующем примере представлены два примера теста с использованием 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"));
    }
}

Что касается тестовых случаев, начните с хорошего случая - делает ли он то, что нужнопредназначены для?Подумайте о граничных условиях.Что произойдет, если вы добавите что-то слишком большое, или слишком маленькое, или нулевое, или пустое и т. Д. Убедитесь, что вы тестируете все ветви - для этого полезен инструмент покрытия кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...