LikeaDuck🦆
1.45K subscribers
65 photos
3 videos
97 links
Дима Тучс (https://t.iss.one/dtuchs). QA директор в DODO, спикер и программный комиттёр на конфах, создатель авторского курса QA.GURU Advanced. Здесь будет об IT, QA, менеджменте и немного обо мне.
Download Telegram
#java #automation

Хозяке java-автоматизаторам на заметку:

Периодически вижу подобные вопросы (и подобный код) тут и там:
Схема ответа:

{
"users": [ 0 ],
"groups": [ 0 ]
}

И класс для его маппинга


@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@JsonIgnoreProperties(ignoreUnknown = true)
public class UserDataRegisterModel {
private Integer type;
private String name;
private String data;
private List<Integer> users;
private List<Integer> groups;
}


Я не могу найти ни одной причины “модельным” классам (точнее, объектам) не быть immutable, уж во всяком случае, в тестах.

А начиная с Java 16 у нас есть ключевое слово record (на самом деле уже с 14, но там это была preview feature). Если вы его еще совсем не использовали, то о том, что это такое, советую послушать Тагира Валеева.

Тот же самый класс без всякого Lombok может выглядеть так:
public record UserDataRegisterModel (
Integer type,
String name,
String data,
List<Integer> users,
List<Integer> groups
) {}

Да, иногда можно придумать (хотя, в действительности, с трудом) поводы менять состояние объекта, но для этого мы вправе написать метод внутри нашего рекорда:
public UserDataRegisterModel addType(int type) {
return new UserDataRegisterModel(type, name, data, users, groups);
}

И пользоваться новым объектом. Ну, все как в любимом нами иммутабельном классе String.

Собственно, в учебном проекте моего курса qa.guru advanced все модели как на бэкендах, так и в тестах существуют в виде record. Чего и вам всем желаю и рекомендую. Вы же не на java 8? Selenide вот требует 17 🙂
21👍5🔥5
#Java #gradle #automation

Вчера один из моих студентов задал вопрос -

Добрый день! Кто может подсказать какой JDK 17 версии нужно поставить чтобы не было конфликтов и можно было использовать selenide 7? Если выбираю JDK 17 версии , то ловлю ошибку "Could not initialize class org.codehaus.groovy.reflection.ReflectionCache"


Ну и скрин с этой ошибкой.

Опыт не пропьешь - я сразу сказал, что, видимо, gradle в проекте / системе не той версии, которая нужна для Java 17 (это версии 7.6 и выше). Студент стал разбираться и - конец немного предсказуем - дело было действительно в старой версии gradle (6.7). Обновили и все заработало.

И тут у меня один вопрос - неужели невозможно, если уж вы делаете разбивки по версиям, которые совместимы с той или иной JDK, неужели совсем невозможно вывести понятное сообщение об ошибке - "Дружище, у тебя несовместимые версии". Как новичок должен разбираться в
Could not initialize class org.codehaus.groovy.reflection.ReflectionCache

?
У меня нет ответа.
👍34🔥83❤‍🔥2
#обучение #java #qaguru

А еще сегодня стартую 4 поток своего авторского курса Advanced. Занятие отрытое для всех, так что приходите, даже если не хотите учиться. Ну а если вдруг думаете - надо оно вам или нет - посмотрите на один из лучших дипломов 3 потока от Миши Нестерова - проект Rococo

Фронтенд (rococo-client) к нему написан нашей командой, а именно Ириной из OZON fintech, но, каждая другая строка кода, каждый микросервис, каждая строчка тестов, и все-все все в этом репозитории - написал Миша, и это и есть наш диплом.

Мы даем front, остальное делают студенты.

На 4-м потоке мы будем давать целых два frontend-а на выбор (REST и GraphQL, совершенно разные проекты), а все бэкенды и тесты будут писать студенты. Будет очень 🚀 интересно 🚀. Промокод в закрепе 😁
🔥19👍3
#Java #Spring

Об обновлении зависимостей.

Обновил тут в одном из своих Spring пет-проектов одну зависимость.
Было 1.1.2, стало - 1.2.1. Поменял местами 2 цифры так сказать, что могло бы пойти не так?

У меня напрочь отвалился Oauth 2.0 code flow.

Запрос /oauth2/token? отдает 400 BAD REQUEST. И говорит вот такое понятное сообщение об ошибке - UNSUPPORTED_GRANT_TYPE. Как же так, спрашиваю сам себя? Ведь я не менял grant_type=authorization_code в своем запросе;
В коде спрингового сервиса даже в DEBUG логгировании нет никаких ошибок и стэктрейсов, потому что эта ошибка и не логгируется при выбрасывании.

Что делать?

Смотреть гит блэйм, кто же и что же поменял в этом OAuth2TokenEndpointFilter, чудес же не бывает? А изменения кроются, на самом деле, внутри OAuth2AuthorizationCodeAuthenticationConverter.

Метод OAuth2EndpointUtils.getFormParameters раньше доставал из запроса и GET-параметры, и URL-encoded параметры, а теперь - только URL-encoded параметры. Как же так, почему раньше-то можно было? Иду в RFC Oauth 2.0 и вот ответ -

The client makes a request to the token endpoint by sending the
following parameters using the "application/x-www-form-urlencoded"


То есть, еще месяц назад реализация OAUTH 2.0 в spring-authorization-server не соответсвовала RFC, принимая GET параметры! Так же ей не соответсвовал и мой код, отправляя GET параметры. Начав бомбить о том "да как так одна циферка зависимости ломает мой прекрасный код!" закончил философским "ошибаются все, и не боги горшки спринговые обжигают".

Тут есть и еще один вопрос, а почему я вообще изначально не прочитал нормально RFC и отправлял GET-параметры?

Потому, что до получения странной ошибки я просто думал, что мне будет достаточно посмотреть воркшоп автора книги Spring Security in Action - и в нем тоже показана отправка простых GET параметров. А значит, ошибаются не только контрибьюторы Spring, но и даже авторы книг о Spring.

Мои выводы
- ошибайтесь на здоровье;
- но, любите дебаг;
- и гитблэймы;
- и чтение спецификаций и RFC
🔥24👏4👍1😁1
#java #opensource

Сегодня я решил переписать allure-grpc.
Это маленькая библиотечка за моим авторством, которая создаёт красивый отчетик для gRPC тестов.

Но она изначально, как говорится, by design, не заточена под bidirectional streaming и на client-streaming - тоже. Стоит признать, что и в реальных проектах это самые редко используемые виды стриминга в gRPC. Она не то что бы совсем с ними не работает, но, если клиент отправляет несколько message, то в отчёте будет несколько steps - для каждого message. Не-кра-си-во.

Пришло время сделать так, чтоб работала одинаково хорошо с любым видом gRPC. Ну и заодно смахнуть пыль с ачивки open source контрибьютора😁

А вы создаете pullrequest-ы в opensource? Делитесь своим опытом🤟
🔥37
#java #opensource

Если вам интересно, что же получилось с Allure-grpc, а там есть, на что посмотреть (потоки, CountDownLatch-и вот это вот все 🥲 А еще unit-тесты!), то, короче говоря, вот PR. Я надеюсь, что это будет полезно всем QA, кто тестирует streaming gRPC.
Это же так радостно, когда есть проблема, и есть библиотека, которая ее решает, не правда ли?
👍95🔥2🏆2
#java #frameworks

Selenide - крутой.

Я думаю, что большинство из вас в этом и так не сильно сомневается (список пользователей говорит сам за себя), но вот пару дней назад произошло маленькое событие, которое могло бы вообще пройти незамеченным для меня, но не прошло: я обновил в одном из своих проектов Selenide c 6.х до последней версии 7.2.1. И у меня перестал компилироваться код🙂 Казалось бы, первая мысль поругаться, но я, наоборот, пишу пост похвалы.

А дело все в том, что разработчики Selenide радикально упростили жизнь всем, кто писал или планирует написать свои кастомные CollectionCondition. Если раньше надо было переопределять 3 метода:
            @Override
public void fail(CollectionSource collection, List<WebElement> elements, Exception lastError, long timeoutMs)

@Override
public boolean missingElementSatisfiesCondition()

@Override
public boolean test(List<WebElement> elements)

Причем, что делает второй и для чего он нужен технически я даже не понимал и просто писал return false;🥲
А еще, надо было писать свои классы исключений: public class SpendsMismatch extends UIAssertionError

А теперь все стало радикально просто, надо переопределить один метод :
            @Override
public CheckResult check(Driver driver, List<WebElement> elements)

Назначение и сигнатура которого понятны даже если вообще в первый раз слышать про CollectionCondition.

Респект всем библиотекам и фреймворкам, которые упрощают свои API и делают их чистыми для понимания и написания кода. Selenide с каждым релизом, обрастая все новым функционалом, становится проще для понимания и расширения. И это хорошо.
Selenide - крутой.
🔥44🥰8👍1
#java #обучение #qaguru

Вчера провел первое занятие уже 5-го потока своего авторского курса Java Advanced в QA.GURU. Как и все предыдущие, курс будет глубоко погружать в разработку и тестирование вокруг Java.

Если вдруг кто-то из вас желает залететь в уходящий поезд 5-го потока, можете воспользоваться моим персональным промокодом DTUCHS10, который даст вам скидку 10% до 25 апреля включительно. Для активации промокода напишите в QA.GURU-SALES.
10
#java

Сегодня впервые воспользовался ключевым словом yield в Java, описывая банальный switch-case:
   return switch (action) {
case ADD -> ...;
case ACCEPT -> ...;
case REJECT -> ...;
case DELETE -> {
userDataClient.remove(username, input.username());
yield ...;
}
};


Я такой - "о, а return из функции -> {} тут уже не работает" и стал смутно вспоминать, что должен же быть какой-то иной способ. Решил погуглить, когда оно там появилось, оказалось что аж в 13 Java - вот так и живешь годами не используя целое ключевое слово.

Вывод - советую читать релиз ноуты того, что в вашем резюме, как бы банально это не казалось 🥲
👍154💯2
#Java #qaguru

Скриншотные тесты - неизбежная необходимость во многих проектах, особенно где есть всякие красивые графики и диаграммы в canvas. Проверяется это только скриншотными тестами, и если мы используем Selenide, то понадобиться еще какая-нибудь библиотека-сравнивалка.

Самая известная из них - ashot от Яндекса.

Тогда наш код может выглядеть примерно так:

assertFalse(
new ImageDiffer().makeDiff(
expectedImage,
ImageIO.read(requireNonNull($("canvas[role='img']").screenshot()))
).hasDiff()
);


где ImageDiffer - класс из ashot, expectedImage - ожидаемое изображение, которое у вас где-то в ресурсах, завернутое в BufferedImage.

Этот код всем неплох (и даже прекрасно прячется внутрь PageObject), но у меня к нему есть вопросы:

1) В случае падения именно этого ассерта, нам нужен совершенно специфичный формат Allure Attachment:
Allure.addAttachment(
"Screenshot diff",
"application/vnd.allure.image.diff",
objectMapper.writeValueAsString(screenDif)
);

2) Нам надо иметь возможность перезаписать expected изображения запустив тест со специальным флагом (например, у нас новый браузер и в нем canvas выглядит слегка по другому).

3) Задачи 1 и 2 хотелось бы делать где-то под капотом, что бы наш код тестов вообще ничего не знал об этих особенностях.

Все эти пункты можно изящно решить с помощью JUnit Extensions, и это один из практических примеров, которые я рассказываю на своем авторском курсе для java автоматизаторов, который стартует буквально на следующей неделе. Наши скриншотные тесты будут простыми, а все, что сложнее ассерта - будет жить в Extension.

Приходите послушать меня и Мишу Рубанова в 54 сериях, я гарантирую, что это будет интересно 🚀
👍259