@ContextConfiguration
- 스프링이 제공하는 애노테이션으로, 스프링 테스트 컨텍스트가 사용할 설정 파일 또는 컨텍스트를 커스터마이징할 수 있는 방법을 제공한다.
ApplicationContextInitializer
- 스프링 ApplicationContext를 프로그래밍으로 초기화 할 때 사용할 수 있는 콜백 인터페이스로, 특정 프로파일을 활성화 하거나, 프로퍼티 소스를 추가하는 등의 작업을 할 수 있다.
TestPropertyValues
- 테스트용 프로퍼티 소스를 정의할 때 사용한다.
Environment
- 스프링 핵심 API로, 프로퍼티와 프로파일을 담당한다.
전체 흐름
- Testcontainer를 사용해서 컨테이너 생성
- ApplicationContextInitializer를 구현하여 생선된 컨테이너에서 정보를 축출하여 Environment에 넣어준다.
- @ContextConfiguration을 사용해서 ApplicationContextInitializer 구현체를 등록한다.
- 테스트 코드에서 Environment, @Value, @ConfigurationProperties 등 다양한 방법으로 해당 프로퍼티를 사용한다.
전체적인 코드
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.times;
import java.util.Optional;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.output.Slf4jLogConsumer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import lombok.extern.slf4j.Slf4j;
import me.whiteship.inflearnthejavatest.domain.Member;
import me.whiteship.inflearnthejavatest.domain.Study;
import me.whiteship.inflearnthejavatest.member.MemberService;
@SpringBootTest
@ExtendWith(MockitoExtension.class)
@ActiveProfiles("test")
@Testcontainers
@Slf4j
@ContextConfiguration(initializers = StudyServiceTest.ContainerPropertyInitializer.class)
class StudyServiceTest {
@Mock MemberService memberService;
@Autowired StudyRepository studyRepository;
@Value("${container.port}") int port;
static Logger log = LoggerFactory.getLogger(StudyServiceTest.class);
@Container
static GenericContainer postgreSQLContainer = new GenericContainer("postgres")
.withExposedPorts(5432)
.withEnv("POSTGRES_DB", "studytest");
@BeforeAll
static void beforeAll() {
Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log);
postgreSQLContainer.followOutput(logConsumer);
}
@BeforeEach
void beforeEach() {
System.out.println("===========");
System.out.println(port);
studyRepository.deleteAll();
}
@Test
void createNewStudy() {
// Given
StudyService studyService = new StudyService(memberService, studyRepository);
assertNotNull(studyService);
Member member = new Member();
member.setId(1L);
member.setEmail("rlawls1991@email.com");
Study study = new Study(10, "테스트");
given(memberService.findById(1L)).willReturn(Optional.of(member));
// When
studyService.createNewStudy(1L, study);
// Then
assertEquals(1L, study.getOwnerId());
then(memberService).should(times(1)).notify(study);
then(memberService).shouldHaveNoMoreInteractions();
}
@DisplayName("다른 사용자가 볼 수 있도록 스터디를 공개한다.")
@Test
void openStudy() {
// Given
StudyService studyService = new StudyService(memberService, studyRepository);
Study study = new Study(10, "더 자바, 테스트");
assertNull(study.getOpenedDateTime());
// When
studyService.openStudy(study);
// Then
assertEquals(StudyStatus.OPENED, study.getStatus());
assertNotNull(study.getOpenedDateTime());
then(memberService).should().notify(study);
}
static class ContainerPropertyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
TestPropertyValues.of("container.port=" + postgreSQLContainer.getMappedPort(5432))
.applyTo(context.getEnvironment());
}
}
}