QA Interview Hub · База знань · Тести

Java: основи

Колекції й тестові дані

List, Set, Map і дженерики в тестових даних

Основні колекції (пакет java.util):

  • List — впорядкований список із дублікатами; доступ за індексом. ArrayList — типовий вибір. Результат findElements, рядки таблиці, елементи відповіді API — зазвичай List.
  • Set — унікальні елементи. HashSet без порядку, LinkedHashSet зберігає порядок вставки, TreeSet — відсортований. Корисно, щоб перевірити відсутність дублікатів: new HashSet<>(list).size() == list.size().
  • Map — пари ключ-значення. HashMap без порядку, LinkedHashMap із порядком вставки, TreeMap за ключем. Зручно для тестових даних за роллю чи оточенням: Map<Role, User>.

Незмінні колекції: List.of(...), Set.of(...), Map.of(...) не дозволяють змін і null; add кидає UnsupportedOperationException. Arrays.asList(...) має фіксований розмір: елемент замінити можна, додати — ні. Якщо потрібна змінювана копія, new ArrayList<>(List.of(...)).

Дженерики (List<String>) переносять перевірку типів у компіляцію: не треба кастів, помилка типу видна одразу. Не використовуйте «сирі» типи (List без параметра). Під час виконання параметр типу стирається (type erasure), тому new T() і instanceof List<String> неможливі; це важливо, коли Jackson десеріалізує List<User> і потрібен TypeReference.

Під час обходу не змінюйте колекцію: видалення елемента в циклі for-each кидає ConcurrentModificationException. Використовуйте removeIf чи ітератор.

hashCode/equals, читання JSON і .properties, Jackson та POJO

Контракт equals/hashCode. Якщо a.equals(b), то a.hashCode() == b.hashCode(). HashMap і HashSet спершу шукають кошик за хешем, а потім порівнюють через equals. Перевизначили лише equals — рівні об’єкти матимуть різні хеші, і set.contains(...) не знайде елемент. Завжди перевизначайте обидва методи разом (або використовуйте record, Lombok @EqualsAndHashCode). Не кладіть у ключі мапи об’єкти, які змінюють поля, що входять у хеш.

Файли тестових даних. Лежать у src/test/resources і потрапляють у classpath. Читайте їх через classpath, а не через відносний шлях, що залежить від робочої теки:

try (InputStream in = getClass().getResourceAsStream("/users.json")) {
    List<User> users = mapper.readValue(in, new TypeReference<List<User>>() {});
}

.properties: Properties props = new Properties(); props.load(in);, значення — props.getProperty("baseUrl", "http://localhost"). Секрети не зберігайте у файлі в репозиторії: беріть зі змінних оточення.

Jackson (ObjectMapper) перетворює JSON у POJO і назад. POJO — звичайний клас (чи record) із полями й акцесорами, без залежності від фреймворку.

  • Потрібен конструктор без аргументів із сетерами, або record, або @JsonCreator.
  • У Jackson 2.x невідоме поле в JSON за замовчуванням дає UnrecognizedPropertyException (FAIL_ON_UNKNOWN_PROPERTIES). Для відповідей API, що можуть розширюватись, додайте @JsonIgnoreProperties(ignoreUnknown = true) або вимкніть цю опцію в мапері. У Jackson 3 ця опція за замовчуванням вимкнена.
  • @JsonProperty("user_id") зв’язує поле з іншою назвою; для LocalDate/Instant у Jackson 2.x потрібен модуль jackson-datatype-jsr310 (у Jackson 3 підтримка java.time вбудована), а @JsonFormat задає формат.
  • ObjectMapper потокобезпечний після конфігурації: створіть один і використовуйте повторно, бо створення дороге.