Multiply Strings - LeetCode with me 42
Следующая задачка уже среднего уровня сложности на длинную арифметику - нужно умножить два числа представленные в виде строк. Применим метод, известный каждому еще со школы - умножение столбиком.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Следующая задачка уже среднего уровня сложности на длинную арифметику - нужно умножить два числа представленные в виде строк. Применим метод, известный каждому еще со школы - умножение столбиком.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Media is too big
VIEW IN TELEGRAM
👍4
Wildcard Matching - LeetCode with me 43
Еще одна сложная задача на написание собственного ограниченного движка регулярных выражений. Сначала попробуем применить наиболее логичный подход - класс Pattern, затем реализуем тривиальное решение, а затем решение на базе динамического программирования.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Еще одна сложная задача на написание собственного ограниченного движка регулярных выражений. Сначала попробуем применить наиболее логичный подход - класс Pattern, затем реализуем тривиальное решение, а затем решение на базе динамического программирования.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Media is too big
VIEW IN TELEGRAM
❤4🔥3👍2
Forwarded from javaswag
https://javaswag.github.io/episode/72/
Слушать подкаст в Apple | Spotify | Yandex
В 72 выпуске подкаста Javaswag поговорили с Александром Барминым о Спринге и архитектуре Необанка
00:00 Начало
05:34 Значение доменной области в разработке
17:28 IBM FileNet и Java EE
22:45 Проблемы и эволюция Java EE
32:50 Spring и Spring Boot
48:10 Миграция между версиями Spring
56:05 Гибкость и сложности Spring Boot
01:01:02 Адаптация Spring к современным трендам
01:04:50 Проблемы зависимости от Spring
01:07:10 Конкуренция и эволюция Spring
01:14:49 Kotlin и Spring: синергия технологий
01:15:44 Эволюция TransferWise в Neobank
01:16:36 Архитектура Wise: микросервисы и AWS
01:19:21 Kubernetes и проблемы распределенных систем
01:24:55 Консистентность и механизмы реконсиляции
01:29:08 Управление микросервисами и версиями
01:33:20 Автоматизация обновления зависимостей
01:37:07 CI/CD и миграции баз данных
01:41:17 Деплой
01:46:49 Непопулярное мнение о языках программирования
01:50:00 Критика Spring Boot и его магии
Гость https://www.linkedin.com/in/abarmin/
Ссылки:
Канал Александра на YouTube - https://www.youtube.com/@ABarmin
Канал Java & Spring Weekly в Telegram - @java_weekly
Wise Tech Stack - https://medium.com/wise-engineering/wise-tech-stack-2022-edition-a6ac089a382f
Spring Cloud с Борисовым - https://youtu.be/4tSyz_v9w7Q
Кип сейф! 🖖
Слушать подкаст в Apple | Spotify | Yandex
В 72 выпуске подкаста Javaswag поговорили с Александром Барминым о Спринге и архитектуре Необанка
00:00 Начало
05:34 Значение доменной области в разработке
17:28 IBM FileNet и Java EE
22:45 Проблемы и эволюция Java EE
32:50 Spring и Spring Boot
48:10 Миграция между версиями Spring
56:05 Гибкость и сложности Spring Boot
01:01:02 Адаптация Spring к современным трендам
01:04:50 Проблемы зависимости от Spring
01:07:10 Конкуренция и эволюция Spring
01:14:49 Kotlin и Spring: синергия технологий
01:15:44 Эволюция TransferWise в Neobank
01:16:36 Архитектура Wise: микросервисы и AWS
01:19:21 Kubernetes и проблемы распределенных систем
01:24:55 Консистентность и механизмы реконсиляции
01:29:08 Управление микросервисами и версиями
01:33:20 Автоматизация обновления зависимостей
01:37:07 CI/CD и миграции баз данных
01:41:17 Деплой
01:46:49 Непопулярное мнение о языках программирования
01:50:00 Критика Spring Boot и его магии
Гость https://www.linkedin.com/in/abarmin/
Ссылки:
Канал Александра на YouTube - https://www.youtube.com/@ABarmin
Канал Java & Spring Weekly в Telegram - @java_weekly
Wise Tech Stack - https://medium.com/wise-engineering/wise-tech-stack-2022-edition-a6ac089a382f
Spring Cloud с Борисовым - https://youtu.be/4tSyz_v9w7Q
Кип сейф! 🖖
Javaswag
#72 - Александр Бармин - эволюция Спринга и архитектура Необанка
В 72 выпуске подкаста Javaswag поговорили с Александром Барминым о Спринге и архитектуре Необанка
🔥12
Jump Game II - LeetCode with me 44
Задача на динамическое программирование. Иногда кажется, что самые быстрые алгоритмы на LeetCode это далеко не самые правильные или подходящие, поэтому рассмотрим сначала хороший алгоритм, а потом быстрый.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Задача на динамическое программирование. Иногда кажется, что самые быстрые алгоритмы на LeetCode это далеко не самые правильные или подходящие, поэтому рассмотрим сначала хороший алгоритм, а потом быстрый.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Media is too big
VIEW IN TELEGRAM
🔥6
Permutations - LeetCode with me 45
Сегодня рассмотрим две задачи на генерацию перестановок. Обе решаются сходным образом - с использованием алгоритма backtracking.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Сегодня рассмотрим две задачи на генерацию перестановок. Обе решаются сходным образом - с использованием алгоритма backtracking.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Media is too big
VIEW IN TELEGRAM
👍7❤🔥2
Rotate Image - LeetCode with me 46
Задача на поворот двухмерного изображения. Берем и поворачиваем.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Задача на поворот двухмерного изображения. Берем и поворачиваем.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Media is too big
VIEW IN TELEGRAM
❤5
Group Anagrams - LeetCode with me 47
Группируем анаграммы вместе. Анаграммы - слова, которые могут быть преобразованы друг в друга путем перестановки букв в них. Очевидное решение с сортировкой работает, но не дает достаточной производительности, поэтому напишем свой несложный алгоритм хеширования.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Группируем анаграммы вместе. Анаграммы - слова, которые могут быть преобразованы друг в друга путем перестановки букв в них. Очевидное решение с сортировкой работает, но не дает достаточной производительности, поэтому напишем свой несложный алгоритм хеширования.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Media is too big
VIEW IN TELEGRAM
👍9❤4
Pow x ^ n - LeetCode with me 48
Реализуем функцию возведения в степень без использования стандартной библиотеки. Для повышения производительности воспользуемся знаниями из школьного курса алгебры.
Это последнее, семидесятое видео в 2024 году. За весь год мои видео посмотрели более 61 тысячи раз, что составляет более 8 тысяч часов. А еще на канал на YouTube подписалось больше 1100 новых зрителей. Довольно неплохой рост, на мой взгляд!
Спасибо всем, кто был со мной в 2024 году, увидимся в новом, 2025 году!
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Реализуем функцию возведения в степень без использования стандартной библиотеки. Для повышения производительности воспользуемся знаниями из школьного курса алгебры.
Это последнее, семидесятое видео в 2024 году. За весь год мои видео посмотрели более 61 тысячи раз, что составляет более 8 тысяч часов. А еще на канал на YouTube подписалось больше 1100 новых зрителей. Довольно неплохой рост, на мой взгляд!
Спасибо всем, кто был со мной в 2024 году, увидимся в новом, 2025 году!
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Media is too big
VIEW IN TELEGRAM
👍10❤3
Уважаемые читатели, поздравляю вас с наступающим Новым годом!🥳
Желаю в новом году решать побольше интересных задач, не терять мотивации и оптимизма.
А еще смотрите мои видео и пишите побольше комментариев 👨💻👩💻
С праздником!🎉
Желаю в новом году решать побольше интересных задач, не терять мотивации и оптимизма.
А еще смотрите мои видео и пишите побольше комментариев 👨💻👩💻
С праздником!
Please open Telegram to view this post
VIEW IN TELEGRAM
🥰15🔥9🎄6🤝2
N-Queens - LeetCode with me 49
Первая задача в новом 2025 году. Начнем с уровня Hard и расставим ферзей по шахматной доске. Ничего сложного, обычный backtracking.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Первая задача в новом 2025 году. Начнем с уровня Hard и расставим ферзей по шахматной доске. Ничего сложного, обычный backtracking.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Media is too big
VIEW IN TELEGRAM
❤4
Maximum Subarray - LeetCode with me 50
Ищем подмассив произвольной длинный с максимальной суммой. Задача решается с использованием скользящего окна или алгоритма Кадано. Также разберем рекурсивный подход, основанный на идее divide and conquer.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Ищем подмассив произвольной длинный с максимальной суммой. Задача решается с использованием скользящего окна или алгоритма Кадано. Также разберем рекурсивный подход, основанный на идее divide and conquer.
Ссылки: Ссылка на задачу | Patreon (если вы не в России) | Boosty (если вы в России) | #LeetCode
Media is too big
VIEW IN TELEGRAM
❤7✍2
Spring Shorts №1 - Embedded Database
Долго думал, как продолжить, с каким материалом вернуться. С одной стороны, старые выпуски были, на мой взгляд, очень полезны - в них я разбирал внутреннее устройство Spring Framework. С другой стороны, возможно, мой темп подходит не всем - слишком медленно или слишком быстро, не видно весь код сразу, нет возможности быстро вернуться к нужному фрагменту и так далее. Попробую немного другой формат - текстовый, назову его Spring Shorts, а сегодня как раз первый выпуск.
Наверняка, многим известно, что если в Spring Boot добавить в качестве зависимости H2, то, по умолчанию, Spring Boot запустит H2 в памяти. Инстанс H2 можно настроить с помощью JDBC URL, который указывается в параметре
Кроме этого, в самом Spring Framework есть возможность создавать in-memory инстансы не только H2, но также HSQL и Derby. Для этого нужно добавить соответствующую зависимость и объявить бин
Зачем может понадобиться отдельный
Долго думал, как продолжить, с каким материалом вернуться. С одной стороны, старые выпуски были, на мой взгляд, очень полезны - в них я разбирал внутреннее устройство Spring Framework. С другой стороны, возможно, мой темп подходит не всем - слишком медленно или слишком быстро, не видно весь код сразу, нет возможности быстро вернуться к нужному фрагменту и так далее. Попробую немного другой формат - текстовый, назову его Spring Shorts, а сегодня как раз первый выпуск.
Наверняка, многим известно, что если в Spring Boot добавить в качестве зависимости H2, то, по умолчанию, Spring Boot запустит H2 в памяти. Инстанс H2 можно настроить с помощью JDBC URL, который указывается в параметре
spring.datasource.url в application.yml или application.properties. Если URL формата jdbc:h2:mem:test, то база данных будет запущена в памяти и все данные будут пропадать каждый раз, когда приложение перезапускается. Если же URL вида jdbc:h2:/data/test, то данные будут храниться на диске и сохраняться между перезапусками приложения. Кроме этого, в самом Spring Framework есть возможность создавать in-memory инстансы не только H2, но также HSQL и Derby. Для этого нужно добавить соответствующую зависимость и объявить бин
DataSource:
@Bean
DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.DERBY)
.build();
}
EmbeddedDatabaseType это enum, в котором есть H2, HSQL и DERBY. Еще можно указать параметр generateUniqueName и при каждом создании бина у базы данных будет уникальное имя, а если указать addScript, то можно добавить имя файла из ресурсов, в котором будет SQL-скрипт для создания структуры таблиц или добавления данных. Зачем может понадобиться отдельный
DataSource с in-memory базой данных? Для тестов, например. А если хотите добавить поддержку свой базы, то можно через setDataSourceFactory передать свой DataSourceFactory, который будет, например, в докере запускать CouchDB.❤11🔥7👍4❤🔥1🥰1💘1
Spring Shorts №2 - SQL Query
Расскажу еще про одну малоизвестную возможность Spring JDBC (не того, который Spring Data JDBC, а обычного). Самым часто используемым компонентом из этого модуля по праву считается
В Spring Data есть отличная фича - при запуске приложения валидирует, правильно ли названы методы и есть ли в базе нужные колонки в таблицах. Hibernate делает то же самое для HQL, а вот у
Сделаю небольшой сетап - у меня будет таблица
Доменный объект
А теперь, запрос на получение всех людей из базы данных:
Все самое интересное - в конструкторе. Компонент зависит от
Вторая часть - маппер в методе
Но как быть, когда в запросе есть параметры? Очень просто - параметры можно объявить и убедиться, что в запросе они точно есть. Выглядит вот так:
Так как это отдельный класс, то можно добавить дополнительные методы, например,
Расскажу еще про одну малоизвестную возможность Spring JDBC (не того, который Spring Data JDBC, а обычного). Самым часто используемым компонентом из этого модуля по праву считается
JdbcTemplate - он и в базу пишет, и читает, и запросы выполняет. В Spring Data есть отличная фича - при запуске приложения валидирует, правильно ли названы методы и есть ли в базе нужные колонки в таблицах. Hibernate делает то же самое для HQL, а вот у
JdbcTemplate такой возможности нет. Зато в Spring JDBC есть класс SqlQuery, который позволяет обернуть обычный SQL запрос в объект и также валидировать при старте приложения. Сделаю небольшой сетап - у меня будет таблица
people, которая создается следующим скриптом:
CREATE TABLE people (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
Доменный объект
Person:
public record Person(
Integer id,
String name,
Instant createdAt) {
public Person(String name) {
this(null, name, Instant.now());
}
public Person withId(int id) {
return new Person(id, name, createdAt);
}
}
А теперь, запрос на получение всех людей из базы данных:
public class FindPeopleQuery extends MappingSqlQuery<Person> {
public FindPeopleQuery(DataSource dataSource) {
super(
dataSource,
"SELECT id, name, created_at FROM people"
);
compileInternal();
}
@Override
protected Person mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Person(
rs.getInt("id"),
rs.getString("name"),
rs.getTimestamp("created_at").toInstant()
);
}
}
Все самое интересное - в конструкторе. Компонент зависит от
DataSource, поэтому мы передаем его в качестве параметра в конструктор, а дальше вызываем родительский конструктор и передаем в него запрос, а затем вызываем compileInternal. compileInternal создает PreparedStatement и в этот-то момент и происходит валидация.
public FindPeopleQuery(DataSource dataSource) {
super(
dataSource,
"SELECT id, name, created_at FROM people"
);
compileInternal();
}
Вторая часть - маппер в методе
mapRow, который конвертирует строку из базы данных в доменный объект. Ну, здесь обычный RowMapper, такой же как и в JdbcTemplate. Но как быть, когда в запросе есть параметры? Очень просто - параметры можно объявить и убедиться, что в запросе они точно есть. Выглядит вот так:
public class FindPersonByIdQuery extends MappingSqlQuery<Person> {
public FindPersonByIdQuery(DataSource dataSource) {
super(
dataSource,
"SELECT id, name, created_at FROM people WHERE id = ?"
);
declareParameter(new SqlParameter("id", Types.INTEGER));
compile();
}
public List<Person> findAll() {
return execute();
}
}
Так как это отдельный класс, то можно добавить дополнительные методы, например,
findAll и из него уже вызывать внутренние методы SqlQuery.🔥11
Spring Shorts №3 - Simple JDBC Insert
В прошлый раз мы посмотрели на
Воспользуемся таблицей из прошлого поста:
Вставлять будем
В конструкторе создаем
И самое главное -
а еще можно завернуть доменный объект в
А вы что используете для доступа к базе данных? Вы все еще кипятите Hibernate? Тогда мы идем к вам!
В прошлый раз мы посмотрели на
SqlQuery, но есть ведь еще SimpleJdbcInsert, который позволяет избежать написания запросов на вставку. Почему именно их? Потому что запросы на вставку довольно большие - insert into table values (column1, column2) values (:value1, :value2), а еще важно не перепутать порядок параметров и колонок. Воспользуемся таблицей из прошлого поста:
CREATE TABLE people (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
Вставлять будем
Person - такой же, как и до этого. А для вставки создадим класс InsertPersonQuery:
public class InsertPersonQuery {
private final SimpleJdbcInsert jdbcInsert;
public InsertPersonQuery(DataSource dataSource) {
this.jdbcInsert = new SimpleJdbcInsert(dataSource)
.withTableName("people")
.usingGeneratedKeyColumns("id")
.usingColumns("name", "created_at");
}
public Person insertPerson(Person person) {
final int generatedId = jdbcInsert.execute(Map.of(
"name", person.name(),
"created_at", person.createdAt()
));
return person.withId(generatedId);
}
}
В конструкторе создаем
SimpleJdbcInsert, указываем, в какую таблицу вставлять и значения каких колонок генерируются автоматически базой данных. Также можно указать имена колонок для вставки. И самое главное -
jdbcInsert.execute. Сюда можно передать Map с параметрами, выглядит вот так:
final int generatedId = jdbcInsert.execute(Map.of(
"name", person.name(),
"created_at", person.createdAt()
));
а еще можно завернуть доменный объект в
BeanPropertySqlParameterSource и тогда Spring будет сам перекладывать значения из объекта в запрос:
public Person insertPerson(Person person) {
final int generatedId = jdbcInsert
.execute(new BeanPropertySqlParameterSource(person));
return person.withId(generatedId);
}
А вы что используете для доступа к базе данных? Вы все еще кипятите Hibernate? Тогда мы идем к вам!
🔥9👍5
Mockito Shorts №1 - Введение
В прошлый раз был Spring, до этого был Spring и вообще почти всегда я пишу и рассказываю про Spring. Пора рассказать о чем-то другом - сегодня будет про Mockito. Да, про библиотеку для замены реальных зависимостей на настраиваемые тестовые. Рассказывать я буду про совместную работу с JUnit 5 и MockitoExtension.
Юнит-тест с Mockito обычно выглядит как-то так:
Чем занимается
1. Перед запуском каждого теста он вызывает
2. После выполнения каждого теста вызывает
3. А еще
Можно обойтись и без
Чтобы создать мок для
Или же создать мок вручную:
Теперь у нас есть мок и для работы его нужно настроить. Сделать это можно через метод
Что будет, если я попытаюсь вызвать
У Mockito достаточно много параметров, которые могут упростить работу и первый из них это smart nullls:
Или
В этом случае сообщение об ошибке будет более развернутое:
Если хочется замокать
В прошлый раз был Spring, до этого был Spring и вообще почти всегда я пишу и рассказываю про Spring. Пора рассказать о чем-то другом - сегодня будет про Mockito. Да, про библиотеку для замены реальных зависимостей на настраиваемые тестовые. Рассказывать я буду про совместную работу с JUnit 5 и MockitoExtension.
Юнит-тест с Mockito обычно выглядит как-то так:
@ExtendWith(MockitoExtension.class)
public class MockitoExtensionDemoTest {
}
Чем занимается
MockitoExtension? Он выполняет несколько действий:1. Перед запуском каждого теста он вызывает
Mockito.mockitoSession().initMocks(this), который для всех свойств с аннотацией @Mock создает и настраивает объект мока, для всех свойств с аннотацией @Spy создает частичный мок (экземпляр класса, обернутый в мок), а также для всех свойств с аннотацией @InjectMocks создает экземпляр и внедряет зависимости. 2. После выполнения каждого теста вызывает
Mockito.validateMockitoUsage(). Если вы видели сообщения вида Unnecessary stubbing или MissingMethodInvocationException, то это результат работы validateMockitoUsage - этот метод проверяет, что все используемые моки правильно настроены и использованы. 3. А еще
MockitoExtension проверяет на наличие на тестовом классе аннотации @MockitoSettings, которая позволяет настроить строгость и не показывать ошибок вида unnecessary stubbing. Можно обойтись и без
MockitoExtension, если создавать моки самостоятельно. Для этого вызываем Mockito.mock() и передаем в качестве параметра класс, для которого нужно создать мок. Например, у меня есть следующие связанные компоненты, которые я хочу использовать в своем тесте:
interface ExecutionContext {
ContextQuestions getQuestions();
}
interface ContextQuestions {
boolean isFeatureEnabled(String featureName);
}
Чтобы создать мок для
ExecutionContext можно создать поле в тесте и поставить на него аннотацию @Mock:
@ExtendWith(MockitoExtension.class)
public class MockitoExtensionDemo2Test {
@Mock
ExecutionContext context;
}
Или же создать мок вручную:
public class MockitoExtensionDemo2Test {
ExecutionContext context;
@BeforeEach
void setUp() {
context = mock(ExecutionContext.class);
}
}
Теперь у нас есть мок и для работы его нужно настроить. Сделать это можно через метод
when:
@Test
void getQuestions_shouldReturnQuestions() {
final ContextQuestions questions = featureName -> true;
when(context.getQuestions()).thenReturn(questions);
assertThat(context.getQuestions()).isNotNull();
}
Что будет, если я попытаюсь вызвать
context.getQuestions().isFeatureEnabled() без вызова when? Очевидно, будет NPE:
@Test
void getQuestions_shouldThrowException() {
assertThatThrownBy(() -> context.getQuestions().isFeatureEnabled("abc"))
.isInstanceOf(NullPointerException.class);
}
У Mockito достаточно много параметров, которые могут упростить работу и первый из них это smart nullls:
@Test
void getQuestions_withSmartNulls() {
context = mock(ExecutionContext.class, Answers.RETURNS_SMART_NULLS);
context.getQuestions().isFeatureEnabled("abc");
}
Или
@Mock(answer = Answers.RETURNS_SMART_NULLS)
ExecutionContext context;
В этом случае сообщение об ошибке будет более развернутое:
You have a NullPointerException here:
… name of the class goes here
because this method call was *not* stubbed correctly:
Если хочется замокать
ContextQuestions, который находится внутри ExecutionContext, то можно создать два мока:
@Test
void getQuestions_nestedMocks() {
ContextQuestions questions = mock(ContextQuestions.class);
when(context.getQuestions()).thenReturn(questions);
when(questions.isFeatureEnabled("abc")).thenReturn(true);
assertThat(context.getQuestions().isFeatureEnabled("abc")).isTrue();
}
🔥5
Обычно так и делается, однако, Mockito может существенно облегчить жизнь, если использовать
Кроме этого, можно сразу настраивать возвращаемые моки:
Ну, и напоследок, можно попросить Mockito вызывать реальные методы вместо моков:
Это все на сегодня, в следующий раз посмотрим, как можно настроить мок с помощью
Anwers.RETURN_DEEP_STUBS. В этом случае код больше не бросает NPE, а возвращает мок, который возвращает значение по умолчанию:
@Test
void getQuestions_withDeepMocks() {
assertThat(context.getQuestions().isFeatureEnabled("abc")).isFalse();
}
Кроме этого, можно сразу настраивать возвращаемые моки:
@Test
void getQuestions_nestedMocks() {
when(context.getQuestions().isFeatureEnabled("abc")).thenReturn(true);
assertThat(context.getQuestions().isFeatureEnabled("abc")).isTrue();
}
Ну, и напоследок, можно попросить Mockito вызывать реальные методы вместо моков:
@ExtendWith(MockitoExtension.class)
class WithRealMethods {
@Mock(answer = Answers.CALLS_REAL_METHODS)
AllEnabledQuestions questions;
@InjectMocks
DefaultExecutionContext context;
@Test
void getQuestions_willCallRealMethods() {
assertThat(context.getQuestions().isFeatureEnabled("abc")).isTrue();
}
}
Это все на сегодня, в следующий раз посмотрим, как можно настроить мок с помощью
thenAnswer вместо thenReturn.👍8🔥4
Продолжим говорить о Mockito. В прошлом посте я рассказал о том, как настроить мок, чтобы он возвращал заранее заданное значение, сегодня расскажу о том, как сделать так, чтобы мок вычислял значение на лету. Для этого нужно использовать
В данном примере мы можем получить доступ к аргументам метода через параметр
Согласитесь, каждый раз писать
В качестве параметра метод
Отдельно нужно сказать, что если метод должен выбрасывать исключение вместо возвращения результата, то можно использовать
В случае, если метод мока ничего не возвращает, то стандартную конструкцию
Спасибо, что читаете мои посты, в заключительном посте расскажу о том, как проверять, с какими параметрами вызывались методы мока.
thenAnswer.
@ExtendWith(MockitoExtension.class)
class WithAnswer {
@Mock
ContextQuestions questions;
@Test
void isFeatureEnabled_withAnswer() {
when(questions.isFeatureEnabled(anyString())).thenAnswer(inv -> {
final String name = inv.getArgument(0, String.class);
return "abc".equals(name) || "xyz".equals(name);
});
assertThat(questions.isFeatureEnabled("abc")).isTrue();
}
}
В данном примере мы можем получить доступ к аргументам метода через параметр
invocation и далее через getArgument. Согласитесь, каждый раз писать
inv.getArgument может быть утомительно, особенно для методов с одним параметром. Чтобы этого избежать можно использовать AdditionalAnswers:
@Test
void isFeatureEnabled_withAnswers() {
when(questions.isFeatureEnabled(anyString())).then(answer((Answer1<Boolean, String>) arg0 ->
"abc".equals(arg0) || “xyz".equals(arg0)));
assertThat(questions.isFeatureEnabled("abc")).isTrue();
}
В качестве параметра метод
answer принимает типизированный интерфейс AnswerX или AnswerVoid, в котором в качестве типов указываются тип возвращаемого значения и тип параметров метода. Таким образом избавились от постоянных вызовов inv.getArgument. Отдельно нужно сказать, что если метод должен выбрасывать исключение вместо возвращения результата, то можно использовать
thenThrow.
@Test
void isFeatureEnabled_withException() {
when(questions.isFeatureEnabled(anyString())).thenThrow(new IllegalStateException("Not implemented"));
assertThatThrownBy(() -> questions.isFeatureEnabled("abc"))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Not implemented");
}
В случае, если метод мока ничего не возвращает, то стандартную конструкцию
when(mock).thenReturn() использовать не получится. Вместо этого нужно использовать конструкции doAnswer, doReturn и doThrow:
@ExtendWith(MockitoExtension.class)
class WithVoidReturnType {
@Mock
DefaultExecutionContext context;
@Test
void doCheck_withAnswer() {
doAnswer(invocation -> {
// Simulate some logic
return null;
}).when(context).doCheck();
context.doCheck();
verify(context).doCheck();
}
}
Спасибо, что читаете мои посты, в заключительном посте расскажу о том, как проверять, с какими параметрами вызывались методы мока.
👍11🔥3❤1
Продолжим говорить о Mockito. Мы уже научились управлять поведением моков, теперь пора поговорить о проверках — с какими параметрами и в каком порядке вызывались методы. В этом посте расскажу о самых полезных инструментах:
✅ verify — как проверить вызов метода
Если вы хотите убедиться, что метод был вызван, используйте verify. Это самый частый способ проверок в Mockito.
Можно также проверять, сколько раз вызывался метод:
Mockito также позволяет проверить и асинхронные вызов - взаимодействие с моком произошло в течение указанного промежутка времени:
🚫 verifyNoInteractions — метод не должен вызываться
Иногда нужно убедиться, что с мок-объектом вообще не взаимодействовали:
А если вы хотите проверить, что после каких-то действий не было дополнительных вызовов, используйте
🔁 InOrder — проверка порядка вызовов
Бывает важно, в каком порядке вызывались методы — тогда пригодится InOrder.
Можно передать в
🎯 @Captor — захват аргументов
Если вы хотите получить значение аргумента, с которым вызывался метод — используйте
Не забудьте аннотацию
Кстати, можно создать
🧪 assertArg — лаконичная проверка аргумента
Начиная с Mockito 5, можно использовать assertArg короткий способ проверить аргумент прямо в verify без использования `ArgumentCaptor`-ов.
Пишите в комментариях, о каких еще интересных возможностях Mockito я не рассказал. А дальше расскажу об одной из моих любимых библиотек для тестирования - AssertJ.
verify, verifyNoInteractions, InOrder, @Captor и assertArg.✅ verify — как проверить вызов метода
Если вы хотите убедиться, что метод был вызван, используйте verify. Это самый частый способ проверок в Mockito.
@Test
void verify_example() {
questions.isFeatureEnabled("abc");
verify(questions).isFeatureEnabled("abc");
}
Можно также проверять, сколько раз вызывался метод:
verify(questions, times(1)).isFeatureEnabled("abc"); // по умолчанию 1
verify(questions, never()).isFeatureEnabled(“xyz”);
verify(questions, atLeastOnce()).isFeatureEnabled(“xyz”);
verify(questions, atLeast(2)).isFeatureEnabled(“xyz");
Mockito также позволяет проверить и асинхронные вызов - взаимодействие с моком произошло в течение указанного промежутка времени:
verify(context, timeout(1000)).doCheck(); // ждать до одной секунды, что произойдет взаимодействие
🚫 verifyNoInteractions — метод не должен вызываться
Иногда нужно убедиться, что с мок-объектом вообще не взаимодействовали:
@Test
void noInteractions_example() {
verifyNoInteractions(questions); // важно: вызовов не было вообще
}
А если вы хотите проверить, что после каких-то действий не было дополнительных вызовов, используйте
verifyNoMoreInteractions:
questions.isFeatureEnabled("abc");
verify(questions).isFeatureEnabled("abc");
verifyNoMoreInteractions(questions); // ни одного вызова больше
🔁 InOrder — проверка порядка вызовов
Бывает важно, в каком порядке вызывались методы — тогда пригодится InOrder.
@Test
void inOrder_example() {
questions.isFeatureEnabled("abc");
questions.isFeatureEnabled("xyz");
InOrder inOrder = inOrder(questions);
inOrder.verify(questions).isFeatureEnabled("abc");
inOrder.verify(questions).isFeatureEnabled("xyz");
}
Можно передать в
inOrder(...) несколько моков и проверять, как они взаимодействовали между собой.🎯 @Captor — захват аргументов
Если вы хотите получить значение аргумента, с которым вызывался метод — используйте
@Captor. Особенно удобно, если метод принимал сложный объект.
@Captor
ArgumentCaptor<String> stringCaptor;
@Test
void captor_example(@Mock ContextQuestions questions) {
questions.isFeatureEnabled("abc");
verify(questions).isFeatureEnabled(stringCaptor.capture());
assertThat(stringCaptor.getValue()).isEqualTo("abc");
}
Не забудьте аннотацию
@ExtendWith(MockitoExtension.class) в классе, чтобы @Captor работал.Кстати, можно создать
ArgumentCaptor прямо в самом тесте и без @Captor:
@Test
void captor_example(@Mock ContextQuestions questions) {
questions.isFeatureEnabled("abc");
final ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
verify(questions).isFeatureEnabled(stringCaptor.capture());
assertThat(stringCaptor.getValue()).isEqualTo("abc");
}
🧪 assertArg — лаконичная проверка аргумента
Начиная с Mockito 5, можно использовать assertArg короткий способ проверить аргумент прямо в verify без использования `ArgumentCaptor`-ов.
@Test
void assertArg_example() {
questions.isFeatureEnabled("abc");
verify(questions).isFeatureEnabled(assertArg(arg -> {
assertThat(arg).startsWith("a");
}));
}
Пишите в комментариях, о каких еще интересных возможностях Mockito я не рассказал. А дальше расскажу об одной из моих любимых библиотек для тестирования - AssertJ.
👍6🔥2
AssertJ Shorts №1
Как и обещал, пост про мою любимую библиотеку для тестов - AssertJ. У проекта есть официальный веб-сайт https://assertj.github.io/doc/, он большой, красивый и там есть масса примеров, как круто можно использовать AssertJ.
Вообще, в JUnit есть и своя библиотека assertion-ов, которая неплохо расширяется, например, Hamcrest-ом. Из плюсов - это просто и доступно из коробки, из минусов - недостаточно выразительно. Что доступно в JUnit:
В билиотеке JUnit есть еще несколько assert-ов, но все они вида assertEqual/assertNotEqual/assertTrue. В AssertJ выбор гораздо больше и все начинается с метода assertThat:
И дальше уже можно по цепочке добавлять проверки. В зависимости от типа параметра доступны различные вспомогательные методы, например, для целых чисел проверки уже другие:
Есть удобная поддержка Optional-ов:
Проверка сложных объектов с AssertJ - это песня! Например, нужно сделать несколько проверок сразу и только если все проходят, только тогда объект удовлетворяет условиям:
Работа с исключениями в AssertJ - просто песня:
А какую библиотеку используете вы в своих проектах?
Как и обещал, пост про мою любимую библиотеку для тестов - AssertJ. У проекта есть официальный веб-сайт https://assertj.github.io/doc/, он большой, красивый и там есть масса примеров, как круто можно использовать AssertJ.
Вообще, в JUnit есть и своя библиотека assertion-ов, которая неплохо расширяется, например, Hamcrest-ом. Из плюсов - это просто и доступно из коробки, из минусов - недостаточно выразительно. Что доступно в JUnit:
String name = "assertJ”;
assertEquals(“assertJ”, name);
В билиотеке JUnit есть еще несколько assert-ов, но все они вида assertEqual/assertNotEqual/assertTrue. В AssertJ выбор гораздо больше и все начинается с метода assertThat:
import static org.assertj.core.api.Assertions.*;
@Test
void basic_assertions() {
String name = "assertj";
assertThat(name)
.startsWith("asser”)
.endsWith("tj")
.isEqualToIgnoringCase("ASSERTJ")
.doesNotContain("mockito");
}
И дальше уже можно по цепочке добавлять проверки. В зависимости от типа параметра доступны различные вспомогательные методы, например, для целых чисел проверки уже другие:
@Test
void number_example() {
assertThat(42)
.isGreaterThan(10)
.isLessThanOrEqualTo(100)
.isNotZero();
}
Есть удобная поддержка Optional-ов:
@Test
void optional_example() {
Optional<String> value = Optional.of("hello");
assertThat(value)
.isPresent()
.contains("hello");
}
@Test
void optional_empty() {
Optional<String> value = Optional.empty();
assertThat(value)
.isEmpty();
}
@Test
void optional_value() {
Object content = new Object();
Optional<Object> value = Optional.of(content);
assertThat(value)
.hasValue(content);
}
Проверка сложных объектов с AssertJ - это песня! Например, нужно сделать несколько проверок сразу и только если все проходят, только тогда объект удовлетворяет условиям:
@Test
void custom_check() {
User user = new User("Alex", 30);
assertThat(user).satisfies(u -> {
assertThat(u.name).startsWith("A");
assertThat(u.age).isBetween(18, 60);
});
}
Работа с исключениями в AssertJ - просто песня:
@Test
void exception_example() {
assertThatThrownBy(() -> {
throw new IllegalArgumentException("bad input");
})
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("bad");
}
А какую библиотеку используете вы в своих проектах?
🔥11👍2❤1
AssertJ Shorts №2 - Collections
Продолжим про AssertJ. Уверен, за неделю вы уже смогли попробовать библиотеку в деле, поэтому расскажу о других интересных возможностях, которые не бросаются в глаза при чтении документации.
Начнем с моей любимой фичи - удобной работе с коллекциями.
Если у вас есть список объектов и вы хотите проверить их свойства — не надо писать stream().map(...). Просто extracting.
Можно вытаскивать сразу несколько полей:
И даже выполнять преобразование на извлеченных значениях:
В моей практике часто нужно проверять и коллекцию из единственного элемента. Здесь у AssertJ есть несколько полезных методов, например, если в коллекции должен быть только один элемент, то к нему можно напрямую обратиться через singleElement:
Если элемент нужно проверить на соблюдение нескольких условий, то можно использовать singleElementSatisfies:
Как быть, если в коллекции есть несколько разношерстных элементов, причем все должны удовлетворять определенному условию, несколько - отдельному условию, и ни один - третьем. И здесь у AssertJ есть отличный набор вспомогательных методов - allMatch, anyMatch и noneMatch:
Ну и напоследок еще несколько полезных методов по работе с коллекциями. Например, проверка размера коллекции, содержимого и порядка элементов в ней:
Коллекцию перед проверкой можно и отфильтровать (и по значению поля тоже):
В заключительной части расскажу о том, как сделать свой собственный custom assert.
Продолжим про AssertJ. Уверен, за неделю вы уже смогли попробовать библиотеку в деле, поэтому расскажу о других интересных возможностях, которые не бросаются в глаза при чтении документации.
Начнем с моей любимой фичи - удобной работе с коллекциями.
Если у вас есть список объектов и вы хотите проверить их свойства — не надо писать stream().map(...). Просто extracting.
record User(String name, int age) {}
@Test
void extracting_example() {
List<User> users = List.of(
new User("Anna", 25),
new User("Bob", 30)
);
assertThat(users)
.extracting(User::name)
.containsExactly("Anna", "Bob");
}
Можно вытаскивать сразу несколько полей:
assertThat(users)
.extracting("name", "age")
.containsExactly(
tuple("Anna", 25),
tuple("Bob", 30)
);
И даже выполнять преобразование на извлеченных значениях:
assertThat(users)
.extracting(u -> u.name.toUpperCase())
.contains("ANNA", "BOB");
В моей практике часто нужно проверять и коллекцию из единственного элемента. Здесь у AssertJ есть несколько полезных методов, например, если в коллекции должен быть только один элемент, то к нему можно напрямую обратиться через singleElement:
@Test
void single_element_example() {
List<String> names = List.of("Alice");
assertThat(names)
.singleElement()
.isEqualTo("Alice");
}
Если элемент нужно проверить на соблюдение нескольких условий, то можно использовать singleElementSatisfies:
@Test
void single_element_satisfies_example() {
List<User> users = List.of(new User("Ann", 21));
assertThat(users).singleElementSatisfies(user -> {
assertThat(user.name).startsWith("A");
assertThat(user.age).isGreaterThan(18);
});
}
Как быть, если в коллекции есть несколько разношерстных элементов, причем все должны удовлетворять определенному условию, несколько - отдельному условию, и ни один - третьем. И здесь у AssertJ есть отличный набор вспомогательных методов - allMatch, anyMatch и noneMatch:
@Test
void any_match_example() {
List<String> names = List.of("Anna", "Bob", "Charlie");
assertThat(names)
.anyMatch(name -> name.length() == 3); // Bob
}
@Test
void all_match_example() {
List<String> names = List.of("Anna", "Alan");
assertThat(names)
.allMatch(name -> name.startsWith("A"));
}
@Test
void none_match_example() {
List<String> names = List.of("Anna", "Bob", "Charlie");
assertThat(names)
.noneMatch(name -> name.length() > 10); // никто не слишком длинный
}
Ну и напоследок еще несколько полезных методов по работе с коллекциями. Например, проверка размера коллекции, содержимого и порядка элементов в ней:
List<String> names = List.of("Anna", "Bob", "Charlie");
assertThat(names)
.hasSize(3)
.contains("Bob", "Anna")
.containsExactly("Anna", "Bob", "Charlie") // и порядок важен!
.doesNotContain("Diana");
Коллекцию перед проверкой можно и отфильтровать (и по значению поля тоже):
List<String> names = List.of("Anna", "Bob", "Alice");
assertThat(names)
.filteredOn(name -> name.startsWith("A"))
.containsExactly("Anna", "Alice");
В заключительной части расскажу о том, как сделать свой собственный custom assert.
🔥10👍4
AssertJ Shorts №3 - Custom Assertion
Кастомные assertions — одна из сильных сторон AssertJ. Они позволяют писать тесты, которые выглядят как читаемый DSL, например:
Разберем по шагам, как сделать собственный assertion. Для начала смоделируем класс, который будем в дальнейшем тестировать:
А теперь приступим как самому assertion - создаем класс, наследуем его от
Здесь (1) - конструктор, который принимает проверяемый объект, а (2) - первый вызов
И в тестах теперь можно написать:
Кроме собственных assertion-ов можно еще создать condition, который используется для проверки элементов коллекции. Подойдет, если одно и то же условие используется во многих местах:
А,
Если условие используется только один раз, то можно использовать match:
О каких еще возможностях библиотеки assertJ вы хотели бы рассказать?
Кастомные assertions — одна из сильных сторон AssertJ. Они позволяют писать тесты, которые выглядят как читаемый DSL, например:
assertThat(user).hasValidEmail().isAdult();
Разберем по шагам, как сделать собственный assertion. Для начала смоделируем класс, который будем в дальнейшем тестировать:
@Value
public class User {
private final String name;
private final String email;
private final int age;
}
А теперь приступим как самому assertion - создаем класс, наследуем его от
AbstractAssert, а в качестве параметров типов указываем класс assertion-а и тот класс, который assertion должен проверять:
import org.assertj.core.api.AbstractAssert;
public class UserAssert extends AbstractAssert<UserAssert, User> {
// (1)
public UserAssert(User actual) {
super(actual, UserAssert.class);
}
// (2)
public static UserAssert assertThat(User actual) {
return new UserAssert(actual);
}
}
Здесь (1) - конструктор, который принимает проверяемый объект, а (2) - первый вызов
assertThat, который создаст экземпляр assertion-а. Теперь остается только добавить методы для проверки:
import org.assertj.core.api.AbstractAssert;
public class UserAssert extends AbstractAssert<UserAssert, User> {
// конструктор и assertThat должны быть здесь
public UserAssert hasValidEmail() {
isNotNull();
if (actual.getEmail() == null || !actual.getEmail().contains("@")) {
failWithMessage("Expected user to have valid email, but was <%s>", actual.getEmail());
}
return this;
}
public UserAssert isAdult() {
isNotNull();
if (actual.getAge() < 18) {
failWithMessage("Expected user to be adult (18+), but was <%s>", actual.getAge());
}
return this;
}
}
И в тестах теперь можно написать:
import static your.package.UserAssert.assertThat;
@Test
void custom_assertion_example() {
User user = new User("Alex", "alex@example.com", 20);
assertThat(user)
.hasValidEmail()
.isAdult();
}
Кроме собственных assertion-ов можно еще создать condition, который используется для проверки элементов коллекции. Подойдет, если одно и то же условие используется во многих местах:
@Test
void condition_example() {
User user = new User("Alice", "alice@example.com", 20);
assertThat(user).has(isAdult);
List<User> users = List.of(
new User("Bob", "bob@texample.com", 17),
new User("Alice", "alice@example.com", 20)
);
assertThat(users).haveExactly(1, isAdult); // один взрослый
}
А,
isAdult в этом случае - отдельный класс:
import org.assertj.core.api.Condition;
Condition<User> isAdult = new Condition<>(
user -> user.age() >= 18,
"an adult (18+)"
);
Если условие используется только один раз, то можно использовать match:
assertThat(user)
.matches(u -> u.age() >= 18, "age is 18+");
О каких еще возможностях библиотеки assertJ вы хотели бы рассказать?
🔥5👍4