Merge branch 'eugenp:master' into JAVA-18149

This commit is contained in:
timis1 2023-02-18 13:45:06 +02:00 committed by GitHub
commit 6373bcc52f
37 changed files with 428 additions and 26 deletions

View File

@ -36,8 +36,8 @@
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>

View File

@ -12,3 +12,4 @@ This module contains articles about Java 14.
- [Java 14 Record Keyword](https://www.baeldung.com/java-record-keyword)
- [New Features in Java 14](https://www.baeldung.com/java-14-new-features)
- [Java 14 Record vs. Lombok](https://www.baeldung.com/java-record-vs-lombok)
- [Record vs. Final Class in Java](https://www.baeldung.com/java-record-vs-final-class)

View File

@ -0,0 +1,62 @@
package com.baeldung.combine2liststomap;
import static java.lang.Math.min;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.junit.jupiter.api.Test;
public class CombineTwoListsInAMapUnitTest {
private static final List<String> KEY_LIST = Arrays.asList("Number One", "Number Two", "Number Three", "Number Four", "Number Five");
private static final List<Integer> VALUE_LIST = Arrays.asList(1, 2, 3, 4, 5);
private static final Map<String, Integer> EXPECTED_MAP = new HashMap<String, Integer>() {{
put("Number One", 1);
put("Number Two", 2);
put("Number Three", 3);
put("Number Four", 4);
put("Number Five", 5);
}};
@Test
void givenTwoLists_whenUsingLoopAndListGet_shouldGetExpectedMap() {
Map<String, Integer> result = new HashMap<>();
int size = KEY_LIST.size();
if (KEY_LIST.size() != VALUE_LIST.size()) {
// throw an exception or print a warning
size = min(KEY_LIST.size(), VALUE_LIST.size());
}
for (int i = 0; i < size; i++) {
result.put(KEY_LIST.get(i), VALUE_LIST.get(i));
}
assertEquals(EXPECTED_MAP, result);
}
@Test
void givenTwoLists_whenUsingStreamApiAndListGet_shouldGetExpectedMap() {
Map<String, Integer> result = IntStream.range(0, KEY_LIST.size())
.boxed()
.collect(Collectors.toMap(KEY_LIST::get, VALUE_LIST::get));
assertEquals(EXPECTED_MAP, result);
}
@Test
void givenTwoLists_whenUsingIterators_shouldGetExpectedMap() {
Map<String, Integer> result = new HashMap<>();
Iterator<String> ik = KEY_LIST.iterator();
Iterator<Integer> iv = VALUE_LIST.iterator();
while (ik.hasNext() && iv.hasNext()) {
result.put(ik.next(), iv.next());
}
assertEquals(EXPECTED_MAP, result);
}
}

View File

@ -24,4 +24,16 @@
</resources>
</build>
<properties>
<awaitility.version>4.2.0</awaitility.version>
</properties>
<dependencies>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>${awaitility.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,35 @@
package com.baeldung.concurrent;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class RequestProcessor {
private Map<String, String> requestStatuses = new HashMap<>();
public String processRequest() {
String requestId = UUID.randomUUID().toString();
requestStatuses.put(requestId, "PROCESSING");
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.schedule((() -> {
requestStatuses.put(requestId, "DONE");
}), getRandomNumberBetween(500, 2000), TimeUnit.MILLISECONDS);
return requestId;
}
public String getStatus(String requestId) {
return requestStatuses.get(requestId);
}
private int getRandomNumberBetween(int min, int max) {
Random random = new Random();
return random.nextInt(max - min) + min;
}
}

View File

@ -0,0 +1,41 @@
package com.baeldung.concurrent;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.concurrent.TimeUnit;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("Request processor")
public class RequestProcessorUnitTest {
RequestProcessor requestProcessor = new RequestProcessor();
@Test
@DisplayName("Wait for completion using Thread.sleep")
void whenWaitingWithThreadSleep_thenStatusIsDone() throws InterruptedException {
String requestId = requestProcessor.processRequest();
Thread.sleep(2000);
assertEquals("DONE", requestProcessor.getStatus(requestId));
}
@Test
@DisplayName("Wait for completion using Awaitility")
void whenWaitingWithAwaitility_thenStatusIsDone() {
String requestId = requestProcessor.processRequest();
Awaitility.await()
.atMost(2, TimeUnit.SECONDS)
.pollDelay(500, TimeUnit.MILLISECONDS)
.until(() -> requestProcessor.getStatus(requestId), not(equalTo("PROCESSING")));
assertEquals("DONE", requestProcessor.getStatus(requestId));
}
}

View File

@ -2,3 +2,4 @@
- [Functional Programming in Java](https://www.baeldung.com/java-functional-programming)
- [Functors in Java](https://www.baeldung.com/java-functors)
- [Callback Functions in Java](https://www.baeldung.com/java-callback-functions)

View File

@ -0,0 +1,2 @@
## Relevant Articles
- [Convert Hex to RGB Using Java](https://www.baeldung.com/java-convert-hex-to-rgb)

View File

@ -5,3 +5,4 @@ This module contains articles about Java HttpClient
### Relevant articles
- [Posting with Java HttpClient](https://www.baeldung.com/java-httpclient-post)
- [Custom HTTP Header With the Java HttpClient](https://www.baeldung.com/java-http-client-custom-header)
- [Java HttpClient Connection Management](https://www.baeldung.com/java-httpclient-connection-management)

View File

@ -0,0 +1,8 @@
package com.baeldung.reflection;
public class PrivateConstructorClass {
private PrivateConstructorClass() {
System.out.println("Used the private constructor!");
}
}

View File

@ -0,0 +1,17 @@
package com.baeldung.reflection;
import java.lang.reflect.Constructor;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class PrivateConstructorUnitTest {
@Test
public void whenConstructorIsPrivate_thenInstanceSuccess() throws Exception {
Constructor<PrivateConstructorClass> pcc = PrivateConstructorClass.class.getDeclaredConstructor();
pcc.setAccessible(true);
PrivateConstructorClass privateConstructorInstance = pcc.newInstance();
Assertions.assertTrue(privateConstructorInstance instanceof PrivateConstructorClass);
}
}

View File

@ -8,3 +8,4 @@
- [Batch Processing of Stream Data in Java](https://www.baeldung.com/java-stream-batch-processing)
- [Stream to Iterable in Java](https://www.baeldung.com/java-stream-to-iterable)
- [Understanding the Difference Between Stream.of() and IntStream.range()](https://www.baeldung.com/java-stream-of-and-intstream-range)
- [Check if Object Is an Array in Java](https://www.baeldung.com/java-check-if-object-is-an-array)

View File

@ -0,0 +1,43 @@
package com.baeldung.streams.intarraytostrings;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class ArrayConversionUtils {
public static void createStreamExample() {
int[] intArray = { 1, 2, 3, 4, 5 };
IntStream intStream = Arrays.stream(intArray);
Integer[] integerArray = { 1, 2, 3, 4, 5 };
Stream<Integer> integerStream = Arrays.stream(integerArray);
}
public static String[] convertToStringArray(Integer[] input) {
return Arrays.stream(input)
.map(Object::toString)
.toArray(String[]::new);
}
public static String[] convertToStringArray(int[] input) {
return Arrays.stream(input)
.mapToObj(Integer::toString)
.toArray(String[]::new);
}
public static String[] convertToStringArrayWithBoxing(int[] input) {
return Arrays.stream(input)
.boxed()
.map(Object::toString)
.toArray(String[]::new);
}
public static String convertToString(int[] input){
return Arrays.stream(input)
.mapToObj(Integer::toString)
.collect(Collectors.joining(", "));
}
}

View File

@ -0,0 +1,52 @@
package com.baeldung.streams.intarraytostrings;
import static com.baeldung.streams.intarraytostrings.ArrayConversionUtils.convertToString;
import static com.baeldung.streams.intarraytostrings.ArrayConversionUtils.convertToStringArray;
import static com.baeldung.streams.intarraytostrings.ArrayConversionUtils.convertToStringArrayWithBoxing;
import org.junit.Assert;
import org.junit.Test;
public class IntArrayToStringUnitTest {
@Test
public void whenConvertingIntegers_thenHandleStreamOfIntegers() {
Integer[] integerNumbers = { 1, 2, 3, 4, 5 };
String[] expectedOutput = { "1", "2", "3", "4", "5" };
String[] strings = convertToStringArray(integerNumbers);
Assert.assertArrayEquals(expectedOutput, strings);
}
@Test
public void whenConvertingInts_thenHandleIntStream() {
int[] intNumbers = { 1, 2, 3, 4, 5 };
String[] expectedOutput = { "1", "2", "3", "4", "5" };
String[] strings = convertToStringArray(intNumbers);
Assert.assertArrayEquals(expectedOutput, strings);
}
@Test
public void givenAnIntArray_whenBoxingToInteger_thenHandleStreamOfIntegers() {
int[] intNumbers = { 1, 2, 3, 4, 5 };
String[] expectedOutput = { "1", "2", "3", "4", "5" };
String[] strings = convertToStringArrayWithBoxing(intNumbers);
Assert.assertArrayEquals(expectedOutput, strings);
}
@Test
public void givenAnIntArray_whenUsingCollectorsJoining_thenReturnCommaSeparatedString(){
int[] intNumbers = { 1, 2, 3, 4, 5 };
String expectedOutput = "1, 2, 3, 4, 5";
String string = convertToString(intNumbers);
Assert.assertEquals(expectedOutput, string);
}
}

View File

@ -9,3 +9,4 @@ This module contains articles about string-related algorithms.
- [Email Validation in Java](https://www.baeldung.com/java-email-validation-regex)
- [Check if the First Letter of a String is Uppercase](https://www.baeldung.com/java-check-first-letter-uppercase)
- [Find the First Non Repeating Character in a String in Java](https://www.baeldung.com/java-find-the-first-non-repeating-character)
- [Find the First Embedded Occurrence of an Integer in a Java String](https://www.baeldung.com/java-string-find-embedded-integer)

View File

@ -59,8 +59,8 @@
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>

View File

@ -94,10 +94,6 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
@ -1175,7 +1171,7 @@
<jhipster-dependencies.version>2.1.1</jhipster-dependencies.version>
<!-- The spring-boot version should match the one managed by
https://mvnrepository.com/artifact/io.github.jhipster/jhipster-dependencies/${jhipster-dependencies.version} -->
<spring-boot.version>2.0.8.RELEASE</spring-boot.version>
<spring-boot.version>2.7.8</spring-boot.version>
<!-- The hibernate version should match the one managed by
https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies/${spring-boot.version} -->
<hibernate.version>5.2.17.Final</hibernate.version>

View File

@ -127,8 +127,8 @@
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- Utility dependencies -->
<dependency>

View File

@ -7,5 +7,5 @@ This module contains articles about RabbitMQ.
- [Exchanges, Queues, and Bindings in RabbitMQ](https://www.baeldung.com/java-rabbitmq-exchanges-queues-bindings)
- [Pub-Sub vs. Message Queues](https://www.baeldung.com/pub-sub-vs-message-queues)
- [Channels and Connections in RabbitMQ](https://www.baeldung.com/java-rabbitmq-channels-connections)
- [Create Dynamic Queues in RabbitMQ](https://www.baeldung.com/rabbitmq-dynamic-queues)

View File

@ -24,6 +24,11 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
@ -89,8 +94,9 @@
<rest-assured.version>3.3.0</rest-assured.version>
<!-- plugins -->
<thin.version>1.0.22.RELEASE</thin.version>
<spring-boot.version>2.7.5</spring-boot.version>
<spring-boot.version>2.7.8</spring-boot.version>
<aspectjweaver.version>1.9.1</aspectjweaver.version>
<mysql-connector-java.version>8.0.31</mysql-connector-java.version>
</properties>
</project>

View File

@ -7,6 +7,7 @@ This module contains articles about querying data using Spring Data JPA.
- [JPA and Hibernate Criteria vs. JPQL vs. HQL Query](https://www.baeldung.com/jpql-hql-criteria-query)
- [Joining Tables With Spring Data JPA Specifications](https://www.baeldung.com/spring-jpa-joining-tables)
- [NonUniqueResultException in Spring Data JPA](https://www.baeldung.com/spring-jpa-non-unique-result-exception)
- [Spring Data Repositories Collections vs. Stream](https://www.baeldung.com/spring-data-collections-vs-stream)
- More articles: [[<-- prev]](../spring-data-jpa-query-2)
### Eclipse Config

View File

@ -8,4 +8,5 @@
- [How to Access EntityManager with Spring Data](https://www.baeldung.com/spring-data-entitymanager)
- [Difference Between JPA and Spring Data JPA](https://www.baeldung.com/spring-data-jpa-vs-jpa)
- [Differences Between Spring Data JPA findFirst() and findTop()](https://www.baeldung.com/spring-data-jpa-findfirst-vs-findtop)
- [Difference Between findBy and findAllBy in Spring Data JPA](https://www.baeldung.com/spring-data-jpa-find-by-vs-find-all-by)
- More articles: [[<-- prev]](../spring-data-jpa-repo)

View File

@ -6,3 +6,4 @@
- [Using a List of Values in a JdbcTemplate IN Clause](https://www.baeldung.com/spring-jdbctemplate-in-list)
- [Obtaining Auto-generated Keys in Spring JDBC](https://www.baeldung.com/spring-jdbc-autogenerated-keys)
- [Spring JDBC Batch Inserts](https://www.baeldung.com/spring-jdbc-batch-inserts)
- [Fix EmptyResultDataAccessException When Using JdbcTemplate](https://www.baeldung.com/jdbctemplate-fix-emptyresultdataaccessexception)

View File

@ -0,0 +1,2 @@
## Relevant Articles
- [Guide to Quarkus Funqy](https://www.baeldung.com/java-quarkus-funqy)

View File

@ -71,8 +71,8 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>test</scope>
</dependency>
<dependency>

View File

@ -2,3 +2,4 @@
### Relevant Articles:
- [Spring Boot 3 and Spring Framework 6.0 Whats New](https://www.baeldung.com/spring-boot-3-spring-6-new)
- [Singleton Design Pattern vs Singleton Beans in Spring Boot](https://www.baeldung.com/spring-boot-singleton-vs-beans)

View File

@ -38,8 +38,8 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -32,8 +32,8 @@
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -25,8 +25,8 @@
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>

View File

@ -41,8 +41,8 @@
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>

View File

@ -20,6 +20,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
@ -59,6 +63,7 @@
<version>${swagger-codegen-maven-plugin.version}</version>
<executions>
<execution>
<id>two-responses</id>
<goals>
<goal>generate</goal>
</goals>
@ -71,6 +76,59 @@
</configOptions>
</configuration>
</execution>
<execution>
<id>dates</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/static/event.yaml</inputSpec>
<language>spring</language>
<configOptions>
<java8>true</java8>
<dateLibrary>custom</dateLibrary>
</configOptions>
<typeMappings>
<typeMapping>DateTime=Instant</typeMapping>
<typeMapping>Date=Date</typeMapping>
</typeMappings>
<importMappings>
<importMapping>Instant=java.time.Instant</importMapping>
<importMapping>Date=java.util.Date</importMapping>
</importMappings>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>${openapi-generator.version}</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<skipValidateSpec>true</skipValidateSpec>
<inputSpec>${project.basedir}/src/main/resources/static/event.yaml</inputSpec>
<generatorName>spring</generatorName>
<configOptions>
<java8>true</java8>
<dateLibrary>custom</dateLibrary>
<openApiNullable>false</openApiNullable>
<interfaceOnly>true</interfaceOnly>
</configOptions>
<typeMappings>
<typeMapping>DateTime=Instant</typeMapping>
<typeMapping>Date=Date</typeMapping>
</typeMappings>
<importMappings>
<importMapping>Instant=java.time.Instant</importMapping>
<importMapping>Date=java.util.Date</importMapping>
</importMappings>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
@ -84,6 +142,7 @@
</build>
<properties>
<openapi-generator.version>6.2.1</openapi-generator.version>
<springfox.version>3.0.0</springfox.version>
<swagger-codegen-maven-plugin.version>3.0.34</swagger-codegen-maven-plugin.version>
<springdoc.version>1.6.10</springdoc.version>

View File

@ -0,0 +1,23 @@
openapi: 3.0.0
info:
title: an example api with dates
version: 0.1.0
paths:
components:
schemas:
Event:
type: object
properties:
organizer:
type: string
startDate:
type: string
format: date
endDate:
type: string
format: date-time
ticketSales:
type: string
description: Beginning of the ticket sales
example: "01-01-2023"
pattern: "[0-9]{2}-[0-9]{2}-[0-9]{4}"

View File

@ -0,0 +1,33 @@
package com.baeldung.dates;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import org.junit.jupiter.api.Test;
import io.swagger.model.Event;
class EventUnitTest {
private static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory()
.getValidator();
@Test
void givenACorrectlyFormattedTicketSales_WhenBuildingEvent_ThenSuccess() {
Set<ConstraintViolation<Event>> violations = VALIDATOR.validate(new Event().ticketSales("01-01-2024"));
assertTrue(violations.isEmpty());
}
@Test
void givenAWronglyFormattedTicketSales_WhenBuildingEvent_ThenSuccess() {
Set<ConstraintViolation<Event>> violations = VALIDATOR.validate(new Event().ticketSales("2024-01-01"));
assertEquals(1, violations.size());
}
}

View File

@ -92,8 +92,8 @@
<version>${javassist.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>

View File

@ -4,3 +4,4 @@
- [Using Environment Variables in Spring Boots application.properties](https://www.baeldung.com/spring-boot-properties-env-variables)
- [Reinitialize Singleton Bean in Spring Context](https://www.baeldung.com/spring-reinitialize-singleton-bean)
- [HTTP Interface in Spring 6](https://www.baeldung.com/spring-6-http-interface)
- [Getting the Current ApplicationContext in Spring](https://www.baeldung.com/spring-get-current-applicationcontext)

View File

@ -11,4 +11,4 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
- [Download a Large File Through a Spring RestTemplate](https://www.baeldung.com/spring-resttemplate-download-large-file)
- [Access HTTPS REST Service Using Spring RestTemplate](https://www.baeldung.com/spring-resttemplate-secure-https-service)
- [Encoding of URI Variables on RestTemplate](https://www.baeldung.com/spring-resttemplate-uri-variables-encode)
- [Difference Between exchange(), postForEntity() and execute() in RestTemplate](https://www.baeldung.com/difference-between-exchange-postForEntity-and-execute)
- [Difference Between exchange(), postForEntity() and execute() in RestTemplate](https://www.baeldung.com/spring-resttemplate-exchange-postforentity-execute)

View File

@ -8,3 +8,4 @@ This module contains articles about JUnit 5 Annotations
- [JUnit5 Programmatic Extension Registration with @RegisterExtension](https://www.baeldung.com/junit-5-registerextension-annotation)
- [Guide to JUnit 5 Parameterized Tests](https://www.baeldung.com/parameterized-tests-junit-5)
- [Writing Templates for Test Cases Using JUnit 5](https://www.baeldung.com/junit5-test-templates)
- [JUnit 5 @Nested Test Classes](https://www.baeldung.com/junit-5-nested-test-classes)