Java for Beginner
673 subscribers
541 photos
155 videos
12 files
827 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Please open Telegram to view this post
VIEW IN TELEGRAM
Написание тестов для CRUD REST API

Пример контроллера
@RestController
@RequestMapping("/api/items")
public class ItemController {

private final ItemService itemService;

@Autowired
public ItemController(ItemService itemService) {
this.itemService = itemService;
}

@GetMapping
public List<Item> getAllItems() {
return itemService.getAllItems();
}

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Item createItem(@RequestBody Item item) {
return itemService.saveItem(item);
}

@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteItem(@PathVariable Long id) {
itemService.deleteItem(id);
}
}


Тестирование CRUD операций
@WebMvcTest(controllers = ItemController.class)
public class ItemControllerTest {

@Autowired
private MockMvc mockMvc;

@MockitoBean
private ItemService itemService;

@Test
public void testGetAllItems() throws Exception {
List<Item> items = Arrays.asList(new Item(1L, "Item1"), new Item(2L, "Item2"));
Mockito.when(itemService.getAllItems()).thenReturn(items);

mockMvc.perform(get("/api/items"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.size()").value(2))
.andExpect(jsonPath("$[0].name").value("Item1"));
}

@Test
public void testCreateItem() throws Exception {
Item item = new Item(1L, "NewItem");
Mockito.when(itemService.saveItem(Mockito.any(Item.class))).thenReturn(item);

mockMvc.perform(post("/api/items")
.content("{\"name\":\"NewItem\"}")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.name").value("NewItem"));
}

@Test
public void testDeleteItem() throws Exception {
mockMvc.perform(delete("/api/items/1"))
.andExpect(status().isNoContent());
}
}


Особенности

@MockitoBean для сервиса:
Изолирует тест от реального сервиса.
Позволяет задавать поведение с помощью Mockito.


Тестирование сериализации/десериализации:
Используйте ObjectMapper для работы с JSON.

Работа с jsonPath:
Удобный способ проверки JSON-структур.

#Java #Training #Spring #Testing #Mockito #TestingWeb
Основы тестирования взаимодействия с базами данных в Spring

Тестирование взаимодействия с базами данных — важная часть разработки приложений на Spring.

Основные аннотации


@DataJpaTest:
Эта аннотация используется для тестирования JPA-репозиториев. Она настраивает in-memory базу данных (например, H2) по умолчанию, но можно настроить и для работы с PostgreSQL.
Автоматически настраивает EntityManager, DataSource и другие компоненты, необходимые для работы с JPA.

@SpringBootTest:
Используется для интеграционного тестирования, когда нужно поднять весь контекст Spring. Подходит для тестирования взаимодействия с реальной базой данных.
Можно указать webEnvironment = SpringBootTest.WebEnvironment.NONE, чтобы не поднимать веб-сервер.


@TestConfiguration:
Позволяет определить дополнительные бины или конфигурации, которые будут использоваться только в тестах.

@Sql:
Позволяет выполнять SQL-скрипты перед или после тестов. Например, для заполнения базы данных тестовыми данными.

@Transactional:
Указывает, что тест должен выполняться в транзакции, которая будет откачена после завершения теста. Это помогает избежать изменений в базе данных между тестами.

@AutoConfigureTestDatabase:
Позволяет заменить in-memory базу данных на реальную (например, PostgreSQL) для тестирования.

Настройка тестовой базы данных

Для тестирования с PostgreSQL нужно:
Добавить зависимость на PostgreSQL в pom.xml или build.gradle:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>


Настроить application-test.properties или application-test.yml для подключения к PostgreSQL:
spring.datasource.url=jdbc:postgresql://localhost:5432/testdb
spring.datasource.username=user
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=create-drop


Пример теста
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Sql(scripts = "/init-test-data.sql")
public class UserRepositoryTest {

@Autowired
private UserRepository userRepository;

@Test
public void testFindByUsername() {
User user = userRepository.findByUsername("testuser");
assertNotNull(user);
assertEquals("testuser", user.getUsername());
}
}


#Java #Training #Spring #Testing #TestingDB
Что выведет код?

public class Task080125_1 {
private static int x = 5;

static {
x = 10;
}

{
x = 15;
}

public Task080125_1() {
System.out.println(x);
}

public static void main(String[] args) {
new Task080125_1();
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
26%
5
19%
10
22%
15
26%
Ошибка компиляции
7%
🫵
Как знакомо... 😠🤣

https://t.me/Java_for_beginner_dev

#Mems
Please open Telegram to view this post
VIEW IN TELEGRAM
Нюансы и продвинутые сценарии тестирования с PostgreSQL

Нюансы тестирования

Использование транзакций:
Аннотация @Transactional в тестах гарантирует, что изменения в базе данных будут откачены после завершения теста. Это важно для изоляции тестов.
Если нужно проверить поведение без транзакций, можно использовать
@Commit или @Rollback(false).

Инициализация данных

Используйте @Sql для выполнения SQL-скриптов перед тестами. Например:
@Sql(scripts = "/init-test-data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)


Для очистки данных после теста:
@Sql(scripts = "/cleanup-test-data.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)


Тестирование миграций:
Если вы используете Flyway или Liquibase, убедитесь, что миграции применяются в тестовой базе данных. Для этого можно использовать @SpringBootTest с настройкой spring.flyway.enabled=true.

Тестирование производительности:
Для тестирования производительности запросов можно использовать @Timed или @Repeat для многократного выполнения тестов.

Пример интеграционного теста
@SpringBootTest
@Transactional
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class UserServiceIntegrationTest {

@Autowired
private UserService userService;

@Test
@Sql(scripts = "/init-test-data.sql")
public void testCreateUser() {
User user = new User();
user.setUsername("newuser");
user.setPassword("password");

User savedUser = userService.createUser(user);
assertNotNull(savedUser.getId());
assertEquals("newuser", savedUser.getUsername());
}
}


Тестирование с использованием Testcontainers

Для более реалистичного тестирования можно использовать Testcontainers, который позволяет запускать PostgreSQL в Docker-контейнере.

Добавьте зависимость Testcontainers:
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.17.6</version>
<scope>test</scope>
</dependency>


Настройте тест:
@SpringBootTest
@Testcontainers
public class UserServiceTestcontainersTest {

@Container
private static final PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:13");

@DynamicPropertySource
static void postgresProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}

@Autowired
private UserService userService;

@Test
public void testCreateUser() {
User user = new User();
user.setUsername("testuser");
user.setPassword("password");

User savedUser = userService.createUser(user);
assertNotNull(savedUser.getId());
assertEquals("testuser", savedUser.getUsername());
}
}


#Java #Training #Spring #Testing #TestingDB
Тестирование сложных сценариев в Spring

Тестирование сложных сценариев в Spring требует понимания работы асинхронного кода, событий и транзакций.

1. Тестирование асинхронного кода

Асинхронные методы, помеченные аннотацией @Async, возвращают CompletableFuture или void. Для их тестирования важно убедиться, что код выполняется в отдельном потоке и завершается корректно.

Пример асинхронного метода:
@Service
public class AsyncService {

@Async
public CompletableFuture<String> asyncMethod() {
// Имитация долгой операции
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return CompletableFuture.completedFuture("Done");
}
}


Тестирование асинхронного метода:

Используйте CountDownLatch для ожидания завершения асинхронной операции.

@SpringBootTest
public class AsyncServiceTest {

@Autowired
private AsyncService asyncService;

@Test
void testAsyncMethod() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
CompletableFuture<String> future = asyncService.asyncMethod();

future.whenComplete((result, ex) -> {
assertThat(result).isEqualTo("Done");
latch.countDown();
});

latch.await(2, TimeUnit.SECONDS); // Ожидаем завершения
assertThat(future.isDone()).isTrue();
}
}
Здесь CountDownLatch используется для синхронизации теста с асинхронным выполнением.


2. Тестирование событий

Spring предоставляет механизм событий через ApplicationEventPublisher. Тестирование событий включает проверку публикации событий и реакции подписчиков.

Пример публикации события:
@Service
public class EventService {

@Autowired
private ApplicationEventPublisher publisher;

public void publishEvent(String message) {
publisher.publishEvent(new CustomEvent(this, message));
}
}

public class CustomEvent extends ApplicationEvent {
private final String message;

public CustomEvent(Object source, String message) {
super(source);
this.message = message;
}

public String getMessage() {
return message;
}
}


Тестирование публикации события:

Используйте ApplicationEvents (из Spring Test) для проверки событий.

@SpringBootTest
public class EventServiceTest {

@Autowired
private EventService eventService;

@Autowired
private ApplicationEvents events;

@Test
void testEventPublication() {
eventService.publishEvent("Test Message");

// Проверяем, что событие было опубликовано
assertThat(events.stream(CustomEvent.class))
.hasSize(1)
.first()
.satisfies(event -> assertThat(event.getMessage()).isEqualTo("Test Message"));
}
}


3. Тестирование транзакций


Транзакции — это ключевой аспект работы с базами данных. Важно убедиться, что транзакции корректно завершаются (commit или rollback).

Пример транзакционного метода:
@Service
public class TransactionalService {

@Autowired
private UserRepository userRepository;

@Transactional
public void createUser(String name) {
User user = new User(name);
userRepository.save(user);
if (name == null) {
throw new IllegalArgumentException("Name cannot be null");
}
}
}


Тестирование rollback

Проверьте, что транзакция откатывается при ошибке.
@SpringBootTest
public class TransactionalServiceTest {

@Autowired
private TransactionalService transactionalService;

@Autowired
private UserRepository userRepository;

@Test
void testRollback() {
assertThatThrownBy(() -> transactionalService.createUser(null))
.isInstanceOf(IllegalArgumentException.class);

// Проверяем, что пользователь не был сохранен
assertThat(userRepository.findAll()).isEmpty();
}
}


#Java #Training #Spring #Testing #Mockito
Что выведет код?

public class Task090125_1 {
public static void main(String[] args) {
int i;
for (; i < 3; i++) {
System.out.print(i + " ");
i = i++;
}
}
}


#Tasks
И чтобы ничего не делать...🤣

https://t.me/Java_for_beginner_dev

#Mems
Please open Telegram to view this post
VIEW IN TELEGRAM
Лучшие практики, советы и нюансы тестирования в Spring

Тестирование — это важная часть разработки приложений на Spring, которая помогает обеспечить стабильность и корректность работы кода. В этом посте мы рассмотрим лучшие практики, советы и нюансы, которые помогут вам писать качественные тесты.

1. Изолируйте тесты

Каждый тест должен быть независимым и не зависеть от состояния, созданного другими тестами. Это позволяет избежать ложных срабатываний и упрощает отладку.

Используйте `@Transactional`:

Аннотация @Transactional автоматически откатывает изменения в базе данных после завершения теста.

  @Test
@Transactional
void testSaveUser() {
User user = new User("John Doe");
userRepository.save(user);
assertThat(userRepository.findAll()).hasSize(1);
}


Очищайте базу данных:

Если вы не используете @Transactional, очищайте базу данных перед каждым тестом с помощью @Sql или вручную.

  @Test
@Sql("/scripts/cleanup.sql")
void testUserCreation() {
// Тестовый код
}


Используйте правильные типы тестов

Unit-тесты:

Тестируйте отдельные компоненты (например, сервисы или репозитории) в изоляции. Используйте моки для зависимостей.

  @ExtendWith(MockitoExtension.class)
public class UserServiceTest {

@Mock
private UserRepository userRepository;

@InjectMocks
private UserService userService;

@Test
void testFindUserById() {
when(userRepository.findById(1L)).thenReturn(Optional.of(new User("John Doe")));
User user = userService.findUserById(1L);
assertThat(user.getName()).isEqualTo("John Doe");
}
}


Интеграционные тесты:

Тестируйте взаимодействие нескольких компонентов (например, сервисов и репозиториев) вместе. Используйте @SpringBootTest.

  @SpringBootTest
public class UserServiceIntegrationTest {

@Autowired
private UserService userService;

@Test
void testCreateUser() {
User user = userService.createUser("Jane Doe");
assertThat(user).isNotNull();
}
}


Используйте Testcontainers для интеграционных тестов

Testcontainers позволяет запускать реальные базы данных (например, PostgreSQL) в Docker-контейнерах. Это делает тесты более приближенными к production-среде.

- Пример:

  @Testcontainers
@SpringBootTest
public class UserRepositoryTest {

@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:13");

@DynamicPropertySource
static void registerPgProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}

@Test
void testSaveUser() {
User user = new User("John Doe");
userRepository.save(user);
assertThat(userRepository.findAll()).hasSize(1);
}
}


#Java #Training #Spring #Testing #Mockito
Тестируйте edge-кейсы

Убедитесь, что ваши тесты покрывают не только "счастливый путь", но и граничные случаи:
- Ошибки валидации.
- Отсутствие данных в базе.
- Нарушение уникальности.
- Исключения в транзакциях.


- Пример:

  @Test
void testUserNotFound() {
assertThatThrownBy(() -> userService.findUserById(999L))
.isInstanceOf(UserNotFoundException.class);
}


Используйте моки для сложных зависимостей

Моки позволяют изолировать тестируемый компонент от внешних зависимостей, таких как базы данных, внешние API или другие сервисы.

- Пример с Mockito:

  @Test
void testSendEmail() {
EmailService emailService = mock(EmailService.class);
UserService userService = new UserService(emailService);

userService.notifyUser("user@example.com");

verify(emailService).sendEmail("user@example.com");
}


Тестируйте асинхронный код

Для тестирования асинхронных методов используйте CompletableFuture и CountDownLatch.

- Пример:

  @Test
void testAsyncMethod() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
CompletableFuture<String> future = asyncService.asyncMethod();

future.whenComplete((result, ex) -> {
assertThat(result).isEqualTo("Done");
latch.countDown();
});

latch.await(2, TimeUnit.SECONDS);
assertThat(future.isDone()).isTrue();
}


Тестируйте события

Spring предоставляет механизм событий через ApplicationEventPublisher. Убедитесь, что события публикуются и обрабатываются корректно.

- Пример:

  @Test
void testEventPublication() {
eventService.publishEvent("Test Message");

assertThat(events.stream(CustomEvent.class))
.hasSize(1)
.first()
.satisfies(event -> assertThat(event.getMessage()).isEqualTo("Test Message"));
}


Оптимизируйте производительность тестов

Минимизируйте контекст Spring:

Используйте
@DataJpaTest, @WebMvcTest и другие специализированные аннотации вместо @SpringBootTest, если это возможно.

Кэшируйте контекст Spring:

Если тесты используют один и тот же контекст, Spring может кэшировать его для ускорения выполнения.

Документируйте тесты

Используйте понятные имена тестов:
Имена тестов должны отражать их цель (например, testCreateUser_withNullName_throwsException).


Добавляйте комментарии:
Если тест сложный, добавьте пояснения, чтобы облегчить понимание.


Автоматизируйте тестирование

Интегрируйте тесты в CI/CD:
Убедитесь, что тесты запускаются автоматически при каждом коммите.

Используйте инструменты для анализа покрытия:
Например, JaCoCo для измерения покрытия кода тестами.

#Java #Training #Spring #Testing #Mockito
Введение в Lombok и базовые аннотации

Всем привет!

Сегодня мы начинаем серию постов, посвященных библиотеке Lombok. Если вы устали писать boilerplate-код (геттеры, сеттеры, toString, equals и т.д.), то Lombok — это то, что вам нужно! Давайте разберемся, что это за библиотека и как она может упростить вашу жизнь.


Что такое Lombok и зачем он нужен?

Lombok — это библиотека для Java, которая помогает сократить объем шаблонного кода. Она автоматически генерирует методы, такие как геттеры, сеттеры, toString(), equals(), hashCode() и другие, во время компиляции. Это делает код более чистым и удобным для чтения.

Пример без Lombok:
public class User {
private String name;
private int age;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}


Пример с Lombok:
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class User {
private String name;
private int age;
}
Lombok убирает необходимость писать шаблонный код, что особенно полезно в больших проектах.



Установка Lombok в проект

Maven (проверяйте версии в библиотеке Maven):

Добавьте зависимость в pom.xml:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
<scope>provided</scope>
</dependency>


Gradle:
Добавьте зависимость в build.gradle:
dependencies {
compileOnly 'org.projectlombok:lombok:1.18.36'
annotationProcessor 'org.projectlombok:lombok:1.18.36'
}


Аннотация @Getter и @Setter

Эти аннотации автоматически генерируют геттеры и сеттеры для всех полей класса.


Пример:
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class User {
private String name;
private int age;
}


Как это работает:
Lombok генерирует методы getName(), setName(), getAge(), setAge() во время компиляции. Эти методы добавляются в байт-код, но не отображаются в исходном коде.


Нюансы:
Можно применять аннотации к отдельным полям, если не нужно генерировать методы для всех полей.

Уровень доступа методов можно изменить с помощью параметра AccessLevel:
@Getter(AccessLevel.PROTECTED)
private String name;


Аннотация @ToString

Эта аннотация автоматически генерирует метод toString(), который возвращает строковое представление объекта.

Пример:
import lombok.ToString;

@ToString
public class User {
private String name;
private int age;
}


Как это работает:
Lombok генерирует метод toString(), который выглядит примерно так:
@Override
public String toString() {
return "User(name=" + this.name + ", age=" + this.age + ")";
}


Нюансы:
Можно исключить поля из toString() с помощью параметра exclude:
@ToString(exclude = "age")
public class User {
private String name;
private int age;
}


Можно включить только определенные поля с помощью параметра of:
@ToString(of = "name")
public class User {
private String name;
private int age;
}


#Java #Training #Spring #Lombok #Getter #Setter #ToString
Что выведет код?

public class Task1001245_1 {
public static void main(String[] args) {
String s1 = "hello";
String s2 = new String("hello");
String s3 = "hello";
String s4 = s2.intern();

System.out.println((s1 == s2) + " " + (s1 == s3) + " " + (s1 == s4));
}
}


#Tasks