-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: integration tests example with extensions
- Loading branch information
Jonathan Henrique Medeiros
committed
Jun 28, 2024
1 parent
9541aef
commit cf3c696
Showing
13 changed files
with
389 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
src/main/resources/db/migration/V0001__inialize-database-schema.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
SET foreign_key_checks = 0; | ||
|
||
TRUNCATE TABLE billionaire; | ||
TRUNCATE TABLE billionaires; | ||
|
||
SET foreign_key_checks = 1; | ||
|
||
INSERT INTO billionaire (first_name, last_name, career, idempotency_id) VALUES ('Aliko', 'Dangote', 'Billionaire Industrialist', UUID()), | ||
('Bill', 'Gates', 'Billionaire Tech Entrepreneur', UUID()), | ||
('Folrunsho', 'Alakija', 'Billionaire Oil Magnate', UUID()); | ||
INSERT INTO billionaires (first_name, last_name, career, idempotency_id) VALUES ('Aliko', 'Dangote', 'Billionaire Industrialist', UUID()), | ||
('Bill', 'Gates', 'Billionaire Tech Entrepreneur', UUID()), | ||
('Folrunsho', 'Alakija', 'Billionaire Oil Magnate', UUID()); |
35 changes: 35 additions & 0 deletions
35
src/test/java/br/com/multidatasources/CleanupDatabaseExtension.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package br.com.multidatasources; | ||
|
||
import org.junit.jupiter.api.extension.BeforeEachCallback; | ||
import org.junit.jupiter.api.extension.ExtensionContext; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.data.repository.CrudRepository; | ||
import org.springframework.stereotype.Repository; | ||
import org.springframework.test.context.ActiveProfiles; | ||
import org.springframework.test.context.junit.jupiter.SpringExtension; | ||
|
||
import java.util.Map; | ||
|
||
@ActiveProfiles("integration-test") | ||
public class CleanupDatabaseExtension implements BeforeEachCallback { | ||
|
||
@Override | ||
public void beforeEach(final ExtensionContext context) { | ||
final var applicationContext = SpringExtension.getApplicationContext(context); | ||
final var repositoryBeans = applicationContext.getBeansWithAnnotation(Repository.class); | ||
cleanupDatabase(repositoryBeans); | ||
} | ||
|
||
private static void cleanupDatabase(final Map<String, Object> repositoryBeans) { | ||
repositoryBeans.values().forEach(CleanupDatabaseExtension::deleteAll); | ||
} | ||
|
||
private static void deleteAll(final Object repository) { | ||
switch (repository) { | ||
case JpaRepository<?, ?> jpaRepository -> jpaRepository.deleteAllInBatch(); | ||
case CrudRepository<?, ?> crudRepository -> crudRepository.deleteAll(); | ||
default -> throw new IllegalArgumentException("Unsupported repository type: " + repository.getClass()); | ||
} | ||
} | ||
|
||
} |
36 changes: 36 additions & 0 deletions
36
src/test/java/br/com/multidatasources/DatabaseRepositoryIntegrationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package br.com.multidatasources; | ||
|
||
import br.com.multidatasources.config.datasource.DataSourceRoutingConfiguration; | ||
import br.com.multidatasources.config.datasource.master.MasterDataSourceConfiguration; | ||
import br.com.multidatasources.config.datasource.replica.ReplicaDataSourceConfiguration; | ||
import br.com.multidatasources.config.properties.datasource.DataSourceConnectionPropertiesConfiguration; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; | ||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; | ||
import org.springframework.context.annotation.Import; | ||
import org.springframework.test.context.ActiveProfiles; | ||
|
||
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; | ||
|
||
@Target(ElementType.TYPE) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Inherited | ||
@ActiveProfiles("integration-test") | ||
@DataJpaTest | ||
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) | ||
@ExtendWith(CleanupDatabaseExtension.class) | ||
@Import( | ||
value = { | ||
MasterDataSourceConfiguration.class, | ||
ReplicaDataSourceConfiguration.class, | ||
DataSourceRoutingConfiguration.class, | ||
DataSourceConnectionPropertiesConfiguration.class | ||
} | ||
) | ||
public @interface DatabaseRepositoryIntegrationTest { | ||
|
||
} |
19 changes: 19 additions & 0 deletions
19
src/test/java/br/com/multidatasources/DefaultBillionaire.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package br.com.multidatasources; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
import java.util.UUID; | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@Target(ElementType.PARAMETER) | ||
public @interface DefaultBillionaire { | ||
|
||
String firstName() default "John"; | ||
|
||
String lastName() default "Doe"; | ||
|
||
String career() default "SWE"; | ||
|
||
} |
40 changes: 40 additions & 0 deletions
40
src/test/java/br/com/multidatasources/DefaultBillionaireParameterResolverExtension.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package br.com.multidatasources; | ||
|
||
import br.com.multidatasources.model.Billionaire; | ||
import br.com.multidatasources.model.factory.BillionaireBuilder; | ||
import org.junit.jupiter.api.extension.ExtensionContext; | ||
import org.junit.jupiter.api.extension.ParameterContext; | ||
import org.junit.jupiter.api.extension.ParameterResolutionException; | ||
import org.junit.jupiter.api.extension.ParameterResolver; | ||
|
||
import java.lang.reflect.Parameter; | ||
|
||
public class DefaultBillionaireParameterResolverExtension implements ParameterResolver { | ||
|
||
@Override | ||
public boolean supportsParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext) throws ParameterResolutionException { | ||
return parameterContext.isAnnotated(DefaultBillionaire.class); | ||
} | ||
|
||
@Override | ||
public Object resolveParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext) throws ParameterResolutionException { | ||
return defaultBillionaire(parameterContext.getParameter()); | ||
} | ||
|
||
private static Billionaire defaultBillionaire(final Parameter parameter) { | ||
if (parameter.getType() != Billionaire.class) { | ||
return null; | ||
} | ||
|
||
final var firstName = parameter.getAnnotation(DefaultBillionaire.class).firstName(); | ||
final var lastName = parameter.getAnnotation(DefaultBillionaire.class).lastName(); | ||
final var career = parameter.getAnnotation(DefaultBillionaire.class).career(); | ||
|
||
return BillionaireBuilder.builder() | ||
.firstName(firstName) | ||
.lastName(lastName) | ||
.career(career) | ||
.build(); | ||
} | ||
|
||
} |
31 changes: 31 additions & 0 deletions
31
src/test/java/br/com/multidatasources/IntegrationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package br.com.multidatasources; | ||
|
||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; | ||
import org.springframework.core.annotation.AliasFor; | ||
import org.springframework.test.context.ActiveProfiles; | ||
|
||
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; | ||
|
||
@Target(ElementType.TYPE) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Inherited | ||
@ActiveProfiles("integration-test") | ||
@SpringBootTest( | ||
webEnvironment = WebEnvironment.NONE | ||
) | ||
@ExtendWith(CleanupDatabaseExtension.class) | ||
public @interface IntegrationTest { | ||
|
||
@AliasFor(annotation = SpringBootTest.class, attribute = "classes") | ||
Class<?>[] classes() default {}; | ||
|
||
@AliasFor(annotation = SpringBootTest.class, attribute = "properties") | ||
String[] properties() default {}; | ||
|
||
} |
70 changes: 70 additions & 0 deletions
70
src/test/java/br/com/multidatasources/repository/BillionaireRepositoryIntegrationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package br.com.multidatasources.repository; | ||
|
||
import br.com.multidatasources.DatabaseRepositoryIntegrationTest; | ||
import br.com.multidatasources.model.factory.BillionaireBuilder; | ||
import br.com.multidatasources.service.v1.idempotency.impl.UUIDIdempotencyGenerator; | ||
import org.apache.commons.lang3.RandomStringUtils; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
|
||
import java.util.UUID; | ||
import java.util.stream.IntStream; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
@DatabaseRepositoryIntegrationTest | ||
class BillionaireRepositoryIntegrationTest { | ||
|
||
@Autowired | ||
private BillionaireRepository billionaireRepository; | ||
|
||
@Test | ||
void givenExistentIdempotencyId_whenExistsBillionaireByIdempotencyId_thenReturnTrue() { | ||
final var idempotencyGenerator = new UUIDIdempotencyGenerator(); | ||
final var billionaire = BillionaireBuilder.builder() | ||
.firstName("John") | ||
.lastName("Doe") | ||
.career("SWE") | ||
.build(); | ||
|
||
billionaire.generateIdempotencyId(idempotencyGenerator); | ||
this.billionaireRepository.save(billionaire); | ||
|
||
final var actual = this.billionaireRepository.existsBillionaireByIdempotencyId(billionaire.getIdempotencyId()); | ||
|
||
assertThat(actual).isTrue(); | ||
} | ||
|
||
@Test | ||
void givenANonExistentIdempotencyId_whenExistsBillionaireByIdempotencyId_thenReturnFalse() { | ||
final var actual = this.billionaireRepository.existsBillionaireByIdempotencyId(UUID.randomUUID()); | ||
assertThat(actual).isFalse(); | ||
} | ||
|
||
@Test | ||
void validateDeleteAllByCallbackExtension() { | ||
// Should be empty table on database | ||
assertThat(this.billionaireRepository.count()).isZero(); | ||
|
||
final var idempotencyGenerator = new UUIDIdempotencyGenerator(); | ||
|
||
// Generate 10 billionaires with random data | ||
final var billionaires = IntStream.range(1, 11) | ||
.mapToObj(index -> { | ||
final var billionaire = BillionaireBuilder.builder() | ||
.firstName(RandomStringUtils.randomAlphabetic(index)) | ||
.lastName(RandomStringUtils.randomAlphabetic(index)) | ||
.career(RandomStringUtils.randomAlphabetic(index)) | ||
.build(); | ||
billionaire.generateIdempotencyId(idempotencyGenerator); | ||
return billionaire; | ||
}).toList(); | ||
|
||
// Save all billionaires | ||
this.billionaireRepository.saveAllAndFlush(billionaires); | ||
|
||
// Should have 10 billionaires on database | ||
assertThat(this.billionaireRepository.count()).isEqualTo(10); | ||
} | ||
|
||
} |
106 changes: 106 additions & 0 deletions
106
src/test/java/br/com/multidatasources/service/v1/BillionaireServiceIntegrationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package br.com.multidatasources.service.v1; | ||
|
||
import br.com.multidatasources.DefaultBillionaire; | ||
import br.com.multidatasources.DefaultBillionaireParameterResolverExtension; | ||
import br.com.multidatasources.IntegrationTest; | ||
import br.com.multidatasources.model.Billionaire; | ||
import br.com.multidatasources.repository.BillionaireRepository; | ||
import br.com.multidatasources.service.v1.idempotency.IdempotencyGenerator; | ||
import br.com.multidatasources.service.v1.idempotency.impl.UUIDIdempotencyGenerator; | ||
import jakarta.persistence.EntityNotFoundException; | ||
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.SpyBean; | ||
|
||
import java.util.List; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
import static org.mockito.Mockito.verify; | ||
|
||
@IntegrationTest | ||
@ExtendWith(DefaultBillionaireParameterResolverExtension.class) | ||
class BillionaireServiceIntegrationTest { | ||
|
||
@SpyBean | ||
private IdempotencyGenerator idempotencyGenerator; | ||
|
||
@Autowired | ||
@SpyBean | ||
private BillionaireRepository billionaireRepository; | ||
|
||
@Autowired | ||
private BillionaireService billionaireService; | ||
|
||
@Test | ||
void givenAValidBillionaireId_whenFindBillionaireById_thenReturnASameBillionaireInformed(@DefaultBillionaire final Billionaire billionaire) { | ||
final var localIdempotencyGenerator = new UUIDIdempotencyGenerator(); | ||
billionaire.generateIdempotencyId(localIdempotencyGenerator); | ||
final var persistedBillionaire = this.billionaireRepository.saveAndFlush(billionaire); | ||
|
||
final var actual = this.billionaireService.findById(persistedBillionaire.getId()); | ||
|
||
assertThat(actual) | ||
.usingRecursiveComparison() | ||
.isEqualTo(persistedBillionaire); | ||
|
||
verify(this.billionaireRepository).findById(persistedBillionaire.getId()); | ||
} | ||
|
||
@Test | ||
void givenAInvalidBillionaireId_whenFindBillionaireById_thenThrowEntityNotFoundException() { | ||
final var invalidBillionaireId = 0L; | ||
|
||
assertThatThrownBy(() -> this.billionaireService.findById(invalidBillionaireId)) | ||
.isInstanceOf(EntityNotFoundException.class) | ||
.hasMessage("Register with id 0 not found"); | ||
|
||
verify(this.billionaireRepository).findById(invalidBillionaireId); | ||
} | ||
|
||
@Test | ||
void givenATwoBillionaires_whenFindAll_thenReturnListWithTwoRegistries( | ||
@DefaultBillionaire final Billionaire billionaireOne, | ||
@DefaultBillionaire(firstName = "Jake") final Billionaire billionaireTwo | ||
) { | ||
final var localIdempotencyGenerator = new UUIDIdempotencyGenerator(); | ||
billionaireOne.generateIdempotencyId(localIdempotencyGenerator); | ||
billionaireTwo.generateIdempotencyId(localIdempotencyGenerator); | ||
this.billionaireRepository.saveAllAndFlush(List.of(billionaireOne, billionaireTwo)); | ||
|
||
final var actual = this.billionaireService.findAll(); | ||
|
||
assertThat(actual) | ||
.hasSize(2) | ||
.usingRecursiveFieldByFieldElementComparator() | ||
.containsExactlyInAnyOrder(billionaireOne, billionaireTwo); | ||
|
||
verify(this.billionaireRepository).findAll(); | ||
} | ||
|
||
@Test | ||
void givenEmptyDataBillionaires_whenFindAll_thenReturnListWithZeroRegistries() { | ||
final var actual = this.billionaireService.findAll(); | ||
assertThat(actual).isEmpty(); | ||
verify(this.billionaireRepository).findAll(); | ||
} | ||
|
||
@Test | ||
void givenANewBillionaire_whenSave_thenReturnASameBillionaireSaved(@DefaultBillionaire final Billionaire billionaire) { | ||
final var localIdempotencyGenerator = new UUIDIdempotencyGenerator(); | ||
billionaire.generateIdempotencyId(localIdempotencyGenerator); | ||
|
||
final var actual = this.billionaireService.save(billionaire); | ||
|
||
assertThat(actual) | ||
.usingRecursiveComparison() | ||
.ignoringFields("id") | ||
.isEqualTo(billionaire); | ||
|
||
verify(this.idempotencyGenerator).generate(billionaire); | ||
verify(this.billionaireRepository).existsBillionaireByIdempotencyId(billionaire.getIdempotencyId()); | ||
verify(this.billionaireRepository).save(billionaire); | ||
} | ||
|
||
} |
Oops, something went wrong.