Skip to content

Commit

Permalink
spring-example : completed
Browse files Browse the repository at this point in the history
  • Loading branch information
maemresen committed Dec 26, 2023
1 parent 8687faa commit 93b296e
Show file tree
Hide file tree
Showing 9 changed files with 372 additions and 0 deletions.
2 changes: 2 additions & 0 deletions modules/02-spring-boot-examples/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ dependencies {
testImplementation("org.springframework.boot:spring-boot-starter-test")

// Testcontainers deps
testImplementation("org.testcontainers:postgresql")
testImplementation("org.testcontainers:junit-jupiter")
testImplementation("org.junit.jupiter:junit-jupiter-params")

// Other (Optional)
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.maeemresen.testcontainers.workshop.spring;

import com.maeemresen.testcontainers.workshop.spring.domain.Person;
import com.maeemresen.testcontainers.workshop.spring.repository.PersonRepository;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.ContainerState;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

@Slf4j
@Testcontainers
@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class StandaloneBasicIT {

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

static {
POSTGRESQL_CONTAINER.withDatabaseName("integration-tests-db");
POSTGRESQL_CONTAINER.withUsername("sa");
POSTGRESQL_CONTAINER.withPassword("sa");
}

@DynamicPropertySource
static void overrideProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", POSTGRESQL_CONTAINER::getJdbcUrl);
registry.add("spring.datasource.username", POSTGRESQL_CONTAINER::getUsername);
registry.add("spring.datasource.password", POSTGRESQL_CONTAINER::getPassword);
registry.add("spring.jpa.hibernate.ddl-auto", () -> "create");
}

@Autowired
private PersonRepository personRepository;

@BeforeEach
void init(final TestInfo testInfo) {
final String containerId = Optional.of(POSTGRESQL_CONTAINER)
.filter(ContainerState::isRunning)
.map(GenericContainer::getContainerId)
.orElse("non-exists");
final String containerInfo = String.format("Container[%s]=%s", "PostgreSQL Container", containerId);
log.info("TEST:{} is using {}", testInfo.getDisplayName(), containerInfo);
}

private Person savePerson(String name) {
var person = new Person();
person.setName(name);
return personRepository.save(person);
}

private String randomName() {
return "name-" + System.currentTimeMillis();
}

@Test
@DisplayName("Standalone Test1")
@Order(1)
void whenFindByNameIgnoreCase_thenReturnPerson() {
String name = randomName();
Person savedPerson = savePerson(name);
var findPerson = personRepository.findTopByNameIgnoreCase(name.toUpperCase());

assertTrue(findPerson.isPresent(), "Person is not found");
assertEquals(savedPerson.getId(), findPerson.get().getId());
}

@Test
@DisplayName("Standalone Test2")
@Order(2)
void whenFindByNameNonExistingName_thenReturnEmpty() {
String name = randomName();
String nonExistingName = "non-existing-name";

savePerson(name);
var findPerson = personRepository.findTopByNameIgnoreCase(nonExistingName);

assertTrue(findPerson.isEmpty(), "Person is found");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.maeemresen.testcontainers.workshop.spring.restartable;

import com.maeemresen.testcontainers.workshop.spring.util.ContainerFactory;
import com.maeemresen.testcontainers.workshop.spring.util.ContainerHolder;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.TestInfo;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.PostgreSQLContainer;

@Slf4j
@ContextConfiguration(initializers = AbstractRestartWithContextPostgresIT.ContextInitializer.class)
@SpringBootTest
public abstract class AbstractRestartWithContextPostgresIT {

public static class ContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
@Override
public void initialize(@NotNull ConfigurableApplicationContext applicationContext) {
try {
GLOBAL_POSTGRESQL_CONTAINER.restart();
} catch (Exception e) {
log.error("Error while restarting the PostgreSQL container", e);
}
}

@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}

protected static final ContainerHolder<PostgreSQLContainer<?>> GLOBAL_POSTGRESQL_CONTAINER
= ContainerFactory.getIntegrationTestDbContainer("RestartWithContext Postgres Container");

@DynamicPropertySource
static void overrideProperties(DynamicPropertyRegistry registry) {
final var container = GLOBAL_POSTGRESQL_CONTAINER.getContainer();
registry.add("spring.datasource.url", container::getJdbcUrl);
registry.add("spring.datasource.username", container::getUsername);
registry.add("spring.datasource.password", container::getPassword);
}

@AfterEach
void init(final TestInfo testInfo) {
log.info("Executed {} successfully.", testInfo.getDisplayName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.maeemresen.testcontainers.workshop.spring.restartable;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.*;
import org.springframework.test.annotation.DirtiesContext;

@Slf4j
@TestClassOrder(ClassOrderer.OrderAnnotation.class)
class RestartableIT {

@BeforeEach
void init(final TestInfo testInfo) {
log.info("TEST: {} is using {}",
testInfo.getDisplayName(),
AbstractRestartWithContextPostgresIT.GLOBAL_POSTGRESQL_CONTAINER.toString());
}

@Nested
@Order(1)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class Suit1 extends AbstractRestartWithContextPostgresIT {

@Test
@DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD)
@DisplayName("Restartable Suit1 - Test1")
@Order(1)
void restartable1Test1() {
}


@Test
@DisplayName("Restartable Suit1 - Test2")
@Order(2)
void restartable1Test2() {
}
}

@Nested
@Order(2)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class Suit2 extends AbstractRestartWithContextPostgresIT {
@Test
@DisplayName("Restartable Suit2 - Test1")
@Order(1)
void restartable2Test1() {

}

@Test
@DisplayName("Restartable Suit2 - Test2")
@Order(2)
void restartable2Test2() {

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.maeemresen.testcontainers.workshop.spring.singletion;

import com.maeemresen.testcontainers.workshop.spring.util.ContainerHolder;
import com.maeemresen.testcontainers.workshop.spring.util.ContainerFactory;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.TestInfo;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.PostgreSQLContainer;

@Slf4j
@SpringBootTest
public abstract class AbstractSingletonPostgresIT {

protected static final ContainerHolder<PostgreSQLContainer<?>> GLOBAL_POSTGRESQL_CONTAINER
= ContainerFactory.getIntegrationTestDbContainer("Singleton Postgres Container");

static {
GLOBAL_POSTGRESQL_CONTAINER.start();
}

@DynamicPropertySource
static void overrideProperties(DynamicPropertyRegistry registry) {
final var container = GLOBAL_POSTGRESQL_CONTAINER.getContainer();
registry.add("spring.datasource.url", container::getJdbcUrl);
registry.add("spring.datasource.username", container::getUsername);
registry.add("spring.datasource.password", container::getPassword);
}

@AfterEach
void init(final TestInfo testInfo){
log.info("Executed {} successfully.", testInfo.getDisplayName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.maeemresen.testcontainers.workshop.spring.singletion;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.*;
import org.springframework.test.annotation.DirtiesContext;

@Slf4j
@TestClassOrder(ClassOrderer.OrderAnnotation.class)
class SingletonIT {

@BeforeEach
void init(final TestInfo testInfo) {
log.info("TEST:{} is using {}",
testInfo.getDisplayName(),
AbstractSingletonPostgresIT.GLOBAL_POSTGRESQL_CONTAINER.toString());
}

@Nested
@Order(1)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class Suit1 extends AbstractSingletonPostgresIT {

@Test
@DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD)
@DisplayName("Singleton Suit1 - Test1")
@Order(1)
void singleton1Test1() {
}


@Test
@DisplayName("Singleton Suit1 - Test2")
@Order(2)
void singleton1Test2() {
}
}

@Nested
@Order(2)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class Suit2 extends AbstractSingletonPostgresIT {
@Test
@DisplayName("Singleton Suit2 - Test1")
@Order(1)
void singleton2Test1() {

}

@Test
@DisplayName("Singleton Suit2 - Test2")
@Order(2)
void singleton2Test2() {

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.maeemresen.testcontainers.workshop.spring.util;

import lombok.experimental.UtilityClass;
import org.testcontainers.containers.PostgreSQLContainer;

@UtilityClass
public class ContainerFactory {
public static <T extends PostgreSQLContainer<T>> ContainerHolder<PostgreSQLContainer<?>> getIntegrationTestDbContainer(final String name) {
try (var container = new PostgreSQLContainer<T>("postgres:13.3")) {
container.withDatabaseName("integration-tests-db");
container.withUsername("sa");
container.withPassword("sa");
return ContainerHolder
.<PostgreSQLContainer<?>>builder()
.name(name)
.container(container)
.build();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.maeemresen.testcontainers.workshop.spring.util;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.testcontainers.containers.ContainerState;
import org.testcontainers.containers.GenericContainer;

import java.util.Optional;

@Slf4j
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
public class ContainerHolder<T extends GenericContainer<?>> {

private final String name;

@Getter
private final T container;

public void restart() {
stop();
start();
}

public void start() {
log.trace("{} is starting", name);
container.start();
log.debug("{} started with instance {}", name, container.getContainerId());
}

public void stop() {
if (container.isCreated() || container.isRunning()) {
log.trace("{} has running instance with id {}. Stopping it.",
name,
container.getContainerId());
final var containerId = container.getContainerId();
container.stop();
log.debug("{} instance stopped.", containerId);
} else {
log.debug("{} has not any running instance to stop.", name);
}
}

@Override
public String toString() {
final String containerId = Optional.ofNullable(container)
.filter(ContainerState::isRunning)
.map(GenericContainer::getContainerId)
.orElse("non-exists");
return String.format("Container[%s]=%s", name, containerId);
}
}

0 comments on commit 93b296e

Please sign in to comment.