Merge branch 'eugenp:master' into master
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
## Relevant Articles
|
||||
- [Native Images with Spring Boot and GraalVM](https://www.baeldung.com/spring-native-intro)
|
||||
- [Ahead of Time Optimizations in Spring 6](https://www.baeldung.com/spring-6-ahead-of-time-optimizations)
|
||||
|
||||
87
spring-boot-modules/spring-boot-3-test-pitfalls/pom.xml
Normal file
87
spring-boot-modules/spring-boot-3-test-pitfalls/pom.xml
Normal file
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-boot-3-test-pitfalls</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>spring-boot-3-test-pitfalls</name>
|
||||
<description>Demo project for Spring Boot Testing Pitfalls</description>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-3</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../parent-boot-3</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<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.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${org.mapstruct.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${org.mapstruct.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok-mapstruct-binding</artifactId>
|
||||
<version>0.2.0</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
|
||||
<maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version>
|
||||
</properties>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.baeldung.sample.pets;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class PetsApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PetsApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.baeldung.sample.pets.boundary;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PetDto {
|
||||
|
||||
private String name;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.baeldung.sample.pets.boundary;
|
||||
|
||||
import com.baeldung.sample.pets.domain.Pet;
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface PetDtoMapper {
|
||||
|
||||
PetDto map(Pet source);
|
||||
|
||||
Pet map(PetDto source);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.baeldung.sample.pets.boundary;
|
||||
|
||||
import com.baeldung.sample.pets.domain.PetService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/pets")
|
||||
@RequiredArgsConstructor
|
||||
public class PetsController {
|
||||
|
||||
private final PetService service;
|
||||
private final PetDtoMapper mapper;
|
||||
|
||||
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public Collection<PetDto> readAll() {
|
||||
return service.getPets().stream()
|
||||
.map(mapper::map)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.baeldung.sample.pets.domain;
|
||||
|
||||
public record Pet(String name) {
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.baeldung.sample.pets.domain;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.Delegate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PetService {
|
||||
|
||||
@Delegate
|
||||
private final PetServiceRepository repo;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.baeldung.sample.pets.domain;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface PetServiceRepository {
|
||||
|
||||
boolean add(Pet pet);
|
||||
|
||||
void clear();
|
||||
|
||||
Collection<Pet> getPets();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.baeldung.sample.pets.domain;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Component
|
||||
public class PetServiceRepositoryImpl implements PetServiceRepository {
|
||||
|
||||
private final Set<Pet> pets = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public Set<Pet> getPets() {
|
||||
return Collections.unmodifiableSet(pets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Pet pet) {
|
||||
return this.pets.add(pet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.pets.clear();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.baeldung.sample.pets.boundary;
|
||||
|
||||
import com.baeldung.sample.pets.domain.PetService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.mock.mockito.MockReset;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
public class PetDtoMapperIntegrationTest {
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackageClasses = PetDtoMapper.class)
|
||||
static class PetDtoMapperTestConfig {
|
||||
|
||||
/*
|
||||
* This would be necessary because the controller is also initialized
|
||||
* and needs the service, although we do not want to test it here.
|
||||
*
|
||||
* Solutions:
|
||||
* - place the mapper into a separate sub package
|
||||
* - do not test the mapper separately, test it integrated within the controller
|
||||
* (recommended)
|
||||
*/
|
||||
@Bean
|
||||
PetService createServiceMock() {
|
||||
return mock(PetService.class, MockReset.withSettings(MockReset.AFTER));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Autowired
|
||||
PetDtoMapper mapper;
|
||||
|
||||
@Test
|
||||
void shouldExist() { // simply test correct test setup
|
||||
assertThat(mapper).isNotNull();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.baeldung.sample.pets.boundary;
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
/**
|
||||
* Just an interface to use for compiler-checked component scanning during tests.
|
||||
* @see ComponentScan#basePackageClasses()
|
||||
*/
|
||||
public interface PetsBoundaryLayer {
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.baeldung.sample.pets.boundary;
|
||||
|
||||
import com.baeldung.sample.pets.domain.PetService;
|
||||
import com.baeldung.sample.test.slices.PetsBoundaryTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@PetsBoundaryTest
|
||||
class PetsControllerMvcIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
MockMvc mvc;
|
||||
@Autowired
|
||||
PetService service;
|
||||
|
||||
@Test
|
||||
void shouldReturnEmptyArrayWhenGetPets() throws Exception {
|
||||
when(service.getPets()).thenReturn(Collections.emptyList());
|
||||
mvc.perform(
|
||||
get("/pets")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
)
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string("[]"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.baeldung.sample.pets.domain;
|
||||
|
||||
import com.baeldung.sample.test.slices.PetsDomainTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@PetsDomainTest
|
||||
class PetServiceIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
PetService service;
|
||||
@Autowired // Mock
|
||||
PetServiceRepository repository;
|
||||
|
||||
@Test
|
||||
void shouldAddPetWhenNotAlreadyExisting() {
|
||||
var pet = new Pet("Dog");
|
||||
when(repository.add(pet)).thenReturn(true);
|
||||
var result = service.add(pet);
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.baeldung.sample.pets.domain;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class PetServiceUnitTest {
|
||||
|
||||
PetService service = new PetService(new PetServiceRepositoryImpl());
|
||||
|
||||
@Test
|
||||
void shouldAddPetWhenNotAlreadyExisting() {
|
||||
var pet = new Pet("Dog");
|
||||
var result = service.add(pet);
|
||||
assertThat(result).isTrue();
|
||||
assertThat(service.getPets()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotAddPetWhenAlreadyExisting() {
|
||||
var pet = new Pet("Cat");
|
||||
var result = service.add(pet);
|
||||
assertThat(result).isTrue();
|
||||
// try a second time
|
||||
result = service.add(pet);
|
||||
assertThat(result).isFalse();
|
||||
assertThat(service.getPets()).hasSize(1);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.baeldung.sample.pets.domain;
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
/**
|
||||
* Just an interface to use for compiler-checked component scanning during tests.
|
||||
* @see ComponentScan#basePackageClasses()
|
||||
*/
|
||||
public interface PetsDomainLayer {
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.baeldung.sample.test.slices;
|
||||
|
||||
import com.baeldung.sample.pets.boundary.PetsBoundaryLayer;
|
||||
import com.baeldung.sample.pets.boundary.PetsController;
|
||||
import com.baeldung.sample.pets.domain.PetService;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.test.mock.mockito.MockReset;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@WebMvcTest(controllers = PetsController.class)
|
||||
@ComponentScan(basePackageClasses = PetsBoundaryLayer.class)
|
||||
@Import(PetsBoundaryTest.PetBoundaryTestConfiguration.class)
|
||||
// further features that can help to configure and execute tests
|
||||
@ActiveProfiles({ "test", "boundary-test" })
|
||||
@Tag("integration-test")
|
||||
@Tag("boundary-test")
|
||||
public @interface PetsBoundaryTest {
|
||||
|
||||
@TestConfiguration
|
||||
class PetBoundaryTestConfiguration {
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
PetService createPetServiceMock() {
|
||||
return mock(
|
||||
PetService.class,
|
||||
MockReset.withSettings(MockReset.AFTER)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.baeldung.sample.test.slices;
|
||||
|
||||
import com.baeldung.sample.pets.domain.PetServiceRepository;
|
||||
import com.baeldung.sample.pets.domain.PetsDomainLayer;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.test.mock.mockito.MockReset;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ComponentScan(basePackageClasses = PetsDomainLayer.class)
|
||||
@Import(PetsDomainTest.PetServiceTestConfiguration.class)
|
||||
// further features that can help to configure and execute tests
|
||||
@ActiveProfiles({"test", "domain-test"})
|
||||
@Tag("integration-test")
|
||||
@Tag("domain-test")
|
||||
public @interface PetsDomainTest {
|
||||
|
||||
@TestConfiguration
|
||||
class PetServiceTestConfiguration {
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
PetServiceRepository createPetsRepositoryMock() {
|
||||
return mock(
|
||||
PetServiceRepository.class,
|
||||
MockReset.withSettings(MockReset.AFTER)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
logging:
|
||||
level:
|
||||
root: info
|
||||
org:
|
||||
springframework:
|
||||
test:
|
||||
context:
|
||||
cache: DEBUG
|
||||
spring:
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
@@ -2,3 +2,4 @@
|
||||
### Relevant Articles:
|
||||
|
||||
- [Spring Boot 3 and Spring Framework 6.0 – What’s 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)
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.baeldung.sample.singleton;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
@Configuration
|
||||
public class SingletonBeanConfig {
|
||||
|
||||
@Bean
|
||||
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
|
||||
public SingletonBean singletonBean() {
|
||||
return new SingletonBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
|
||||
public SingletonBean anotherSingletonBean() {
|
||||
return new SingletonBean();
|
||||
}
|
||||
|
||||
static class SingletonBean {
|
||||
public String getValue() {
|
||||
return "test";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.baeldung.sample.singleton;
|
||||
|
||||
public final class ThreadSafeSingleInstance {
|
||||
|
||||
private static volatile ThreadSafeSingleInstance instance = null;
|
||||
|
||||
private ThreadSafeSingleInstance() {}
|
||||
|
||||
public static ThreadSafeSingleInstance getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized(ThreadSafeSingleInstance.class) {
|
||||
if (instance == null) {
|
||||
instance = new ThreadSafeSingleInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.baeldung.sample.singleton;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class SingletonBeanUnitTest {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("singletonBean")
|
||||
private SingletonBeanConfig.SingletonBean beanOne;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("singletonBean")
|
||||
private SingletonBeanConfig.SingletonBean beanTwo;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("anotherSingletonBean")
|
||||
private SingletonBeanConfig.SingletonBean beanThree;
|
||||
|
||||
@Test
|
||||
void givenTwoBeansWithSameId_whenInjectingThem_thenSameInstancesAreReturned() {
|
||||
assertSame(beanOne, beanTwo);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenTwoBeansWithDifferentId_whenInjectingThem_thenDifferentInstancesAreReturned() {
|
||||
assertNotSame(beanOne, beanThree);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.baeldung.sample.singleton;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
|
||||
class ThreadSafeSingleInstanceUnitTest {
|
||||
|
||||
@Test
|
||||
void givenTwoSingletonInstances_whenGettingThem_thenSameInstancesAreReturned() {
|
||||
ThreadSafeSingleInstance instanceOne = ThreadSafeSingleInstance.getInstance();
|
||||
ThreadSafeSingleInstance instanceTwo = ThreadSafeSingleInstance.getInstance();
|
||||
assertSame(instanceOne, instanceTwo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -2,5 +2,5 @@ package com.baeldung.boot.readonlyrepository;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface BookRepository extends BookReadOnlyRepository, CrudRepository<Book, Long> {
|
||||
public interface BookRepository extends CrudRepository<Book, Long> {
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>jaxb2-maven-plugin</artifactId>
|
||||
<version>2.5.0</version>
|
||||
<version>2.3.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>xjc</id>
|
||||
@@ -89,7 +89,7 @@
|
||||
<configuration>
|
||||
<packageName>com.baeldung</packageName>
|
||||
<sources>
|
||||
<source>src/main/resources/products.xsd</source>
|
||||
<source>/${project.basedir}/src/main/resources/products.xsd</source>
|
||||
</sources>
|
||||
|
||||
</configuration>
|
||||
|
||||
@@ -7,3 +7,4 @@ This module contains articles about logging in Spring Boot projects with Log4j 2
|
||||
- [Logging to Graylog with Spring Boot](https://www.baeldung.com/graylog-with-spring-boot)
|
||||
- [Log Groups in Spring Boot 2.1](https://www.baeldung.com/spring-boot-log-groups)
|
||||
- [Writing Log Data to Syslog Using Log4j2](https://www.baeldung.com/log4j-to-syslog)
|
||||
- [Spring Boot Logback and Log4j2 Extensions](https://www.baeldung.com/spring-boot-logback-log4j2)
|
||||
|
||||
@@ -9,13 +9,11 @@
|
||||
<packaging>jar</packaging>
|
||||
<description>Module For Spring Boot Integration with BIRT</description>
|
||||
|
||||
<!-- this module should use the Boot parent directly to avoid inherit -->
|
||||
<!-- the logback dependency from our other parents -->
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.1.1.RELEASE</version>
|
||||
<relativePath /> <!-- lookup parent from repository -->
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>parent-boot-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../../parent-boot-2</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}"
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user