← Назад к вопросам

Какие будешь писать юнит-тесты при реализации интерфейса List

1.7 Middle🔥 111 комментариев
#Коллекции#Тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Какие будешь писать юнит-тесты при реализации интерфейса List

При реализации интерфейса List нужно покрыть все основные операции и edge case'ы.

Полный набор unit-тестов

@DisplayName("CustomList Tests")
class CustomListTest {
    
    private CustomList<Integer> list;
    
    @BeforeEach
    void setUp() {
        list = new CustomList<>();
    }
    
    // ===== TESTS FOR add() =====
    
    @Test
    @DisplayName("add() should add element to empty list")
    void testAddToEmptyList() {
        boolean result = list.add(1);
        
        assertTrue(result);
        assertEquals(1, list.size());
        assertEquals(1, list.get(0));
    }
    
    @Test
    @DisplayName("add() should add multiple elements")
    void testAddMultipleElements() {
        list.add(1);
        list.add(2);
        list.add(3);
        
        assertEquals(3, list.size());
        assertEquals(1, list.get(0));
        assertEquals(2, list.get(1));
        assertEquals(3, list.get(2));
    }
    
    @Test
    @DisplayName("add() should handle resize when capacity exceeded")
    void testAddWithResize() {
        // Добавляем 15 элементов (дефолт capacity 10)
        for (int i = 0; i < 15; i++) {
            list.add(i);
        }
        
        assertEquals(15, list.size());
        assertEquals(14, list.get(14));
    }
    
    @Test
    @DisplayName("add() should accept null")
    void testAddNull() {
        boolean result = list.add(null);
        
        assertTrue(result);
        assertEquals(1, list.size());
        assertNull(list.get(0));
    }
    
    @Test
    @DisplayName("add() should allow duplicates")
    void testAddDuplicates() {
        list.add(1);
        list.add(1);
        list.add(1);
        
        assertEquals(3, list.size());
    }
    
    // ===== TESTS FOR get() =====
    
    @Test
    @DisplayName("get() should return element at index")
    void testGetElementAtIndex() {
        list.add(10);
        list.add(20);
        list.add(30);
        
        assertEquals(20, list.get(1));
    }
    
    @Test
    @DisplayName("get() should throw IndexOutOfBoundsException for negative index")
    void testGetNegativeIndex() {
        list.add(1);
        
        assertThrows(IndexOutOfBoundsException.class, () -> list.get(-1));
    }
    
    @Test
    @DisplayName("get() should throw IndexOutOfBoundsException for index >= size")
    void testGetIndexOutOfBounds() {
        list.add(1);
        
        assertThrows(IndexOutOfBoundsException.class, () -> list.get(1));
    }
    
    // ===== TESTS FOR remove() =====
    
    @Test
    @DisplayName("remove(Object) should remove element by value")
    void testRemoveByValue() {
        list.add(1);
        list.add(2);
        list.add(3);
        
        boolean result = list.remove(Integer.valueOf(2));
        
        assertTrue(result);
        assertEquals(2, list.size());
        assertEquals(1, list.get(0));
        assertEquals(3, list.get(1));
    }
    
    @Test
    @DisplayName("remove(Object) should return false if element not found")
    void testRemoveNotFound() {
        list.add(1);
        list.add(2);
        
        boolean result = list.remove(Integer.valueOf(999));
        
        assertFalse(result);
        assertEquals(2, list.size());
    }
    
    @Test
    @DisplayName("remove(Object) should handle null")
    void testRemoveNull() {
        list.add(1);
        list.add(null);
        list.add(3);
        
        boolean result = list.remove(null);
        
        assertTrue(result);
        assertEquals(2, list.size());
        assertEquals(1, list.get(0));
        assertEquals(3, list.get(1));
    }
    
    @Test
    @DisplayName("remove(int) should remove element by index")
    void testRemoveByIndex() {
        list.add(10);
        list.add(20);
        list.add(30);
        
        Integer removed = list.remove(1);
        
        assertEquals(20, removed);
        assertEquals(2, list.size());
        assertEquals(30, list.get(1));
    }
    
    @Test
    @DisplayName("remove(int) should throw for invalid index")
    void testRemoveInvalidIndex() {
        list.add(1);
        
        assertThrows(IndexOutOfBoundsException.class, () -> list.remove(5));
    }
    
    // ===== TESTS FOR size() =====
    
    @Test
    @DisplayName("size() should return 0 for empty list")
    void testSizeEmpty() {
        assertEquals(0, list.size());
    }
    
    @Test
    @DisplayName("size() should reflect changes after add/remove")
    void testSizeChanges() {
        assertEquals(0, list.size());
        
        list.add(1);
        assertEquals(1, list.size());
        
        list.add(2);
        list.add(3);
        assertEquals(3, list.size());
        
        list.remove(0);
        assertEquals(2, list.size());
    }
    
    // ===== TESTS FOR isEmpty() =====
    
    @Test
    @DisplayName("isEmpty() should return true for empty list")
    void testIsEmptyTrue() {
        assertTrue(list.isEmpty());
    }
    
    @Test
    @DisplayName("isEmpty() should return false for non-empty list")
    void testIsEmptyFalse() {
        list.add(1);
        assertFalse(list.isEmpty());
    }
    
    // ===== TESTS FOR contains() =====
    
    @Test
    @DisplayName("contains() should return true if element exists")
    void testContainsTrue() {
        list.add(1);
        list.add(2);
        list.add(3);
        
        assertTrue(list.contains(2));
    }
    
    @Test
    @DisplayName("contains() should return false if element not exists")
    void testContainsFalse() {
        list.add(1);
        list.add(2);
        
        assertFalse(list.contains(999));
    }
    
    @Test
    @DisplayName("contains() should handle null")
    void testContainsNull() {
        list.add(1);
        list.add(null);
        list.add(3);
        
        assertTrue(list.contains(null));
    }
    
    // ===== TESTS FOR indexOf() =====
    
    @Test
    @DisplayName("indexOf() should return correct index")
    void testIndexOf() {
        list.add(10);
        list.add(20);
        list.add(30);
        
        assertEquals(1, list.indexOf(20));
    }
    
    @Test
    @DisplayName("indexOf() should return -1 if not found")
    void testIndexOfNotFound() {
        list.add(1);
        
        assertEquals(-1, list.indexOf(999));
    }
    
    @Test
    @DisplayName("indexOf() should return first occurrence")
    void testIndexOfFirstOccurrence() {
        list.add(1);
        list.add(2);
        list.add(2);
        list.add(3);
        
        assertEquals(1, list.indexOf(2));
    }
    
    // ===== TESTS FOR iterator() =====
    
    @Test
    @DisplayName("iterator() should iterate all elements")
    void testIterator() {
        list.add(1);
        list.add(2);
        list.add(3);
        
        List<Integer> result = new ArrayList<>();
        for (Integer element : list) {
            result.add(element);
        }
        
        assertEquals(List.of(1, 2, 3), result);
    }
    
    @Test
    @DisplayName("iterator() should work on empty list")
    void testIteratorEmpty() {
        assertFalse(list.iterator().hasNext());
    }
    
    @Test
    @DisplayName("iterator.next() should throw NoSuchElementException when exhausted")
    void testIteratorNextThrows() {
        list.add(1);
        
        Iterator<Integer> iter = list.iterator();
        iter.next();
        
        assertThrows(NoSuchElementException.class, iter::next);
    }
    
    @Test
    @DisplayName("iterator.remove() should remove element")
    void testIteratorRemove() {
        list.add(1);
        list.add(2);
        list.add(3);
        
        Iterator<Integer> iter = list.iterator();
        iter.next(); // 1
        iter.next(); // 2
        iter.remove(); // удаляем 2
        
        assertEquals(2, list.size());
        assertEquals(1, list.get(0));
        assertEquals(3, list.get(1));
    }
    
    // ===== STRESS TESTS =====
    
    @Test
    @DisplayName("stress test: add 10000 elements")
    void testAddManyElements() {
        for (int i = 0; i < 10000; i++) {
            list.add(i);
        }
        
        assertEquals(10000, list.size());
        assertEquals(9999, list.get(9999));
    }
    
    @Test
    @DisplayName("stress test: performance of get() is O(1)")
    void testGetPerformance() {
        for (int i = 0; i < 1000; i++) {
            list.add(i);
        }
        
        long start = System.nanoTime();
        for (int i = 0; i < 1000; i++) {
            list.get(i);
        }
        long duration = System.nanoTime() - start;
        
        // Должно быть быстро (O(1) доступ)
        assertTrue(duration < 1_000_000_000); // < 1 второй
    }
}

Требуемое покрытие

  • Все public методы — 100% покрытие
  • Happy path — стандартные операции
  • Edge cases — пустой список, null, граница размера
  • Error cases — исключения
  • Stress tests — производительность
  • Integration — комбинированные операции

Best Practices

  1. @DisplayName для читаемых описаний тестов
  2. @BeforeEach для инициализации перед каждым тестом
  3. assertTrue/assertFalse для boolean проверок
  4. assertEquals для проверки значений
  5. assertThrows для проверки исключений
  6. Параметризованные тесты для разных типов данных
  7. Граничные условия (empty, null, first/last element)
  8. Инварианты — неизменяемые свойства
  9. Комбинированные операции — add->remove->add
  10. Производительность — O(1) get(), O(n) add/remove