diff --git a/build.gradle b/build.gradle index dd370c44..02283446 100644 --- a/build.gradle +++ b/build.gradle @@ -52,6 +52,11 @@ dependencies { // Test testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation "org.junit.jupiter:junit-jupiter:5.8.1" + testImplementation "org.testcontainers:testcontainers:2.0.1" + testImplementation "org.testcontainers:testcontainers-junit-jupiter:2.0.1" + testImplementation "org.testcontainers:testcontainers-mysql:2.0.1" + testImplementation 'org.springframework.boot:spring-boot-testcontainers' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' // Swagger diff --git a/src/test/java/leets/weeth/config/TestContainersConfig.java b/src/test/java/leets/weeth/config/TestContainersConfig.java new file mode 100644 index 00000000..017c3895 --- /dev/null +++ b/src/test/java/leets/weeth/config/TestContainersConfig.java @@ -0,0 +1,19 @@ +package leets.weeth.config; + +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; +import org.springframework.context.annotation.Bean; +import org.testcontainers.mysql.MySQLContainer; +import org.testcontainers.utility.DockerImageName; + +@TestConfiguration +public class TestContainersConfig { + private static final String MYSQL_IMAGE = "mysql:8.0.41"; + + @Bean + @ServiceConnection + public MySQLContainer mysqlContainer() { + return new MySQLContainer(DockerImageName.parse(MYSQL_IMAGE)) + .withReuse(true); + } +} diff --git a/src/test/java/leets/weeth/config/TestContainersTest.java b/src/test/java/leets/weeth/config/TestContainersTest.java new file mode 100644 index 00000000..131cce93 --- /dev/null +++ b/src/test/java/leets/weeth/config/TestContainersTest.java @@ -0,0 +1,25 @@ +package leets.weeth.config; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +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.testcontainers.mysql.MySQLContainer; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +@DataJpaTest +@Import(TestContainersConfig.class) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +class TestContainersTest { + @Autowired + private MySQLContainer mysqlContainer; + + @Test + void 설정파일로_주입된_컨테이너_정상_동작_테스트() { + assertThat(mysqlContainer).isNotNull(); + assertThat(mysqlContainer.isRunning()).isTrue(); + System.out.println("Container JDBC URL: " + mysqlContainer.getJdbcUrl()); + } +} diff --git a/src/test/java/leets/weeth/domain/schedule/domain/repository/EventRepositoryTest.java b/src/test/java/leets/weeth/domain/schedule/domain/repository/EventRepositoryTest.java new file mode 100644 index 00000000..3f6e2548 --- /dev/null +++ b/src/test/java/leets/weeth/domain/schedule/domain/repository/EventRepositoryTest.java @@ -0,0 +1,82 @@ +package leets.weeth.domain.schedule.domain.repository; + +import leets.weeth.config.TestContainersConfig; +import leets.weeth.domain.schedule.domain.entity.Event; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import(TestContainersConfig.class) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +class EventRepositoryTest { + + @Autowired + private EventRepository eventRepository; + + @BeforeEach + void setUp() { + LocalDateTime now = LocalDateTime.now(); + Event event1 = Event.builder() + .title("Event 1") + .start(now.plusDays(1)) + .end(now.plusDays(2)) + .cardinal(1) + .build(); + + Event event2 = Event.builder() + .title("Event 2") + .start(now.plusDays(3)) + .end(now.plusDays(4)) + .cardinal(1) + .build(); + + Event event3 = Event.builder() + .title("Event 3") + .start(now.plusDays(5)) + .end(now.plusDays(6)) + .cardinal(2) + .build(); + + eventRepository.saveAll(List.of(event1, event2, event3)); + } + + @Test + void testFindByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc() { + // given + LocalDateTime start = LocalDateTime.now().plusDays(2); + LocalDateTime end = LocalDateTime.now().plusDays(7); + + // when + List events = eventRepository.findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(end, start); + + // then + assertThat(events) + .hasSize(2) + .extracting(Event::getTitle) + .containsExactly("Event 2", "Event 3"); // 순서와 구성이 모두 일치하는지 + } + + @Test + void testFindAllByCardinal() { + // given + int cardinal = 1; + + // when + List events = eventRepository.findAllByCardinal(cardinal); + + // then + assertThat(events) + .hasSize(2) + .extracting(Event::getTitle) + .containsExactly("Event 1", "Event 2"); + } +} diff --git a/src/test/java/leets/weeth/domain/schedule/domain/repository/MeetingRepositoryTest.java b/src/test/java/leets/weeth/domain/schedule/domain/repository/MeetingRepositoryTest.java new file mode 100644 index 00000000..9e64aba1 --- /dev/null +++ b/src/test/java/leets/weeth/domain/schedule/domain/repository/MeetingRepositoryTest.java @@ -0,0 +1,150 @@ +package leets.weeth.domain.schedule.domain.repository; + +import leets.weeth.config.TestContainersConfig; +import leets.weeth.domain.schedule.domain.entity.Meeting; +import leets.weeth.domain.schedule.domain.entity.enums.MeetingStatus; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +@Import(TestContainersConfig.class) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +class MeetingRepositoryTest { + @Autowired + private MeetingRepository meetingRepository; + + @BeforeEach + void setUp() { + Meeting meeting1 = Meeting.builder() + .title("Meeting 1") + .start(LocalDateTime.now().plusDays(1)) + .end(LocalDateTime.now().plusDays(2)) + .code(1111) + .cardinal(1) + .meetingStatus(MeetingStatus.OPEN) + .build(); + + Meeting meeting2 = Meeting.builder() + .title("Meeting 2") + .start(LocalDateTime.now().plusDays(3)) + .end(LocalDateTime.now().plusDays(4)) + .code(2222) + .cardinal(1) + .meetingStatus(MeetingStatus.OPEN) + .build(); + + Meeting meeting3 = Meeting.builder() + .title("Meeting 3") + .start(LocalDateTime.now().plusDays(5)) + .end(LocalDateTime.now().plusDays(6)) + .code(3333) + .cardinal(2) + .meetingStatus(MeetingStatus.CLOSE) + .build(); + + meetingRepository.saveAll(java.util.List.of(meeting1, meeting2, meeting3)); + } + + + @Test + void findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc() { + // given + LocalDateTime start = LocalDateTime.now().plusDays(1); + LocalDateTime end = LocalDateTime.now().plusDays(4); + + // when + List meetings = meetingRepository.findByStartLessThanEqualAndEndGreaterThanEqualOrderByStartAsc(end, start); + + // then + assertThat(meetings) + .hasSize(2) + .extracting(Meeting::getTitle) + .containsExactly("Meeting 1", "Meeting 2"); + } + + @Test + void findAllByCardinalOrderByStartAsc() { + // given + int cardinal = 1; + + // when + List meetings = meetingRepository.findAllByCardinalOrderByStartAsc(cardinal); + + // then + assertThat(meetings) + .hasSize(2) + .extracting(Meeting::getTitle) + .containsExactly("Meeting 1", "Meeting 2"); + } + + @Test + void findAllByCardinalOrderByStartDesc() { + // given + int cardinal = 1; + + // when + List meetings = meetingRepository.findAllByCardinalOrderByStartDesc(cardinal); + + // then + assertThat(meetings) + .hasSize(2) + .extracting(Meeting::getTitle) + .containsExactly("Meeting 2", "Meeting 1"); + } + + @Test + void findAllByCardinal() { + // given + int cardinal = 1; + + // when + List meetings = meetingRepository.findAllByCardinal(cardinal); + + // then + assertThat(meetings) + .hasSize(2) + .extracting(Meeting::getTitle) + .containsExactlyInAnyOrder("Meeting 1", "Meeting 2"); + } + + @Test + void findAllByMeetingStatusAndEndBeforeOrderByEndAsc() { + // given + MeetingStatus status = MeetingStatus.OPEN; + + // when + List meetings = meetingRepository.findAllByMeetingStatusAndEndBeforeOrderByEndAsc(status, LocalDateTime.now().plusDays(5)); + + // then + assertThat(meetings) + .hasSize(2) + .extracting(Meeting::getTitle) + .containsExactly("Meeting 1", "Meeting 2"); + + assertThat(meetings) + .extracting(Meeting::getMeetingStatus) + .containsOnly(MeetingStatus.OPEN) + .doesNotContain(MeetingStatus.CLOSE); + } + + @Test + void findAllByOrderByStartDesc() { + // when + List meetings = meetingRepository.findAllByOrderByStartDesc(); + + // then + assertThat(meetings) + .hasSize(3) + .extracting(Meeting::getTitle) + .containsExactly("Meeting 3", "Meeting 2", "Meeting 1"); + } +} diff --git a/src/test/java/leets/weeth/domain/schedule/domain/service/EventGetServiceTest.java b/src/test/java/leets/weeth/domain/schedule/domain/service/EventGetServiceTest.java new file mode 100644 index 00000000..2a06a7ca --- /dev/null +++ b/src/test/java/leets/weeth/domain/schedule/domain/service/EventGetServiceTest.java @@ -0,0 +1,67 @@ +package leets.weeth.domain.schedule.domain.service; + +import leets.weeth.domain.schedule.application.exception.EventNotFoundException; +import leets.weeth.domain.schedule.application.mapper.ScheduleMapper; +import leets.weeth.domain.schedule.domain.entity.Event; +import leets.weeth.domain.schedule.domain.repository.EventRepository; +import leets.weeth.domain.schedule.test.fixture.ScheduleTestFixture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class EventGetServiceTest { + + @Mock + private EventRepository eventRepository; + + @Mock + private ScheduleMapper scheduleMapper; + + @InjectMocks + private EventGetService eventGetService; + + private Event testEvent; + + @BeforeEach + void setUp() { + testEvent = ScheduleTestFixture.createEvent(); + } + + @Test + void testFind() { + // given + when(eventRepository.findById(1L)) + .thenReturn(Optional.of(testEvent)); + + // when + Event event = eventGetService.find(1L); + + // then + assertThat(event).isEqualTo(testEvent); + verify(eventRepository).findById(1L); + } + + @Test + void testFindNotFound() { + // given + Long id = 999L; + when(eventRepository.findById(id)).thenReturn(Optional.empty()); + + // when & then + // 이벤트가 존재하지 않을 때 예외가 발생하는지 확인 + assertThatThrownBy(() -> eventGetService.find(id)) + .isInstanceOf(EventNotFoundException.class); + verify(eventRepository).findById(id); + } +} diff --git a/src/test/java/leets/weeth/domain/schedule/domain/service/MeetingGetServiceTest.java b/src/test/java/leets/weeth/domain/schedule/domain/service/MeetingGetServiceTest.java new file mode 100644 index 00000000..992bd62e --- /dev/null +++ b/src/test/java/leets/weeth/domain/schedule/domain/service/MeetingGetServiceTest.java @@ -0,0 +1,71 @@ +package leets.weeth.domain.schedule.domain.service; + +import leets.weeth.domain.schedule.application.exception.MeetingNotFoundException; +import leets.weeth.domain.schedule.application.mapper.ScheduleMapper; +import leets.weeth.domain.schedule.domain.entity.Meeting; +import leets.weeth.domain.schedule.domain.repository.MeetingRepository; +import leets.weeth.domain.schedule.test.fixture.ScheduleTestFixture; +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.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class MeetingGetServiceTest { + + @Mock + private MeetingRepository meetingRepository; + + @Mock + private ScheduleMapper scheduleMapper; + + @InjectMocks + private MeetingGetService meetingGetService; + + private Meeting testMeeting; + + @BeforeEach + void setUp() { + testMeeting = ScheduleTestFixture.createMeeting(); + } + + @Test + @DisplayName("[MeetingGetService] 기본 조회 메서드 테스트") + void testFind() { + // given + when(meetingRepository.findById(1L)) + .thenReturn(Optional.of(testMeeting)); + + + // when + Meeting meeting = meetingGetService.find(1L); + + + // when + assertThat(meeting).isEqualTo(testMeeting); + verify(meetingRepository).findById(1L); + } + + @Test + @DisplayName("[MeetingGetService] null인 경우 예외 발생 테스트") + void testFindNotFound() { + // given + when(meetingRepository.findById(1L)) + .thenReturn(Optional.empty()); + + // when & then + assertThatThrownBy(() -> meetingGetService.find(1L)) + .isInstanceOf(MeetingNotFoundException.class); + verify(meetingRepository).findById(1L); + } +} diff --git a/src/test/java/leets/weeth/domain/schedule/test/fixture/ScheduleTestFixture.java b/src/test/java/leets/weeth/domain/schedule/test/fixture/ScheduleTestFixture.java new file mode 100644 index 00000000..284e4f1e --- /dev/null +++ b/src/test/java/leets/weeth/domain/schedule/test/fixture/ScheduleTestFixture.java @@ -0,0 +1,30 @@ +package leets.weeth.domain.schedule.test.fixture; + +import leets.weeth.domain.schedule.domain.entity.Event; +import leets.weeth.domain.schedule.domain.entity.Meeting; + +import java.time.LocalDateTime; + +public class ScheduleTestFixture { + + public static Event createEvent() { + return Event.builder() + .title("Test Meeting") + .location("Test Location") + .start(LocalDateTime.now()) + .end(LocalDateTime.now().plusDays(2)) + .cardinal(1) + .build(); + } + + public static Meeting createMeeting() { + return Meeting.builder() + .title("Test Meeting") + .location("Test Location") + .start(LocalDateTime.now()) + .end(LocalDateTime.now().plusDays(2)) + .code(1234) + .cardinal(1) + .build(); + } +} diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml new file mode 100644 index 00000000..4747e28c --- /dev/null +++ b/src/test/resources/application-test.yml @@ -0,0 +1,11 @@ +spring: + profiles: + active: test + jpa: + hibernate: + ddl-auto: create-drop + show-sql: true + properties: + hibernate: + format_sql: true + dialect: org.hibernate.dialect.MySQL8Dialect