Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,21 @@
import kr.touroot.authentication.dto.response.TokenResponse;
import kr.touroot.authentication.fixture.OauthUserFixture;
import kr.touroot.authentication.helper.LoginTestHelper;
import kr.touroot.authentication.infrastructure.JwtTokenProvider;
import kr.touroot.authentication.infrastructure.KakaoOauthProvider;
import kr.touroot.global.AcceptanceTest;
import kr.touroot.global.IntegrationTest;
import kr.touroot.global.AbstractControllerIntegrationTest;
import kr.touroot.member.domain.Member;
import kr.touroot.utils.DatabaseCleaner;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.web.server.LocalServerPort;

@DisplayName("로그인 컨트롤러")
@AcceptanceTest
class LoginControllerTest extends IntegrationTest {
class LoginControllerTest extends AbstractControllerIntegrationTest {

@Autowired
private JwtTokenProvider jwtTokenProvider;
@Autowired
private DatabaseCleaner databaseCleaner;
@Autowired
private LoginTestHelper testHelper;
@MockBean
private KakaoOauthProvider oauthProvider;
Comment on lines 28 to 29
Copy link
Member

@nak-honest nak-honest Dec 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MockBean 으로 인해 로그인 컨트롤러와 로그인 서비스 테스트는 각각 스프링 컨텍스트를 새로 띄우네요!
혹시 @MockBean 들도 추상 클래스에 protected 로 재정의하여 전체 컨텍스트를 2개로 줄이는 건 어떻게 생각하실까요??

다만 MemberRepository 가 전체 서비스 테스트에서 mock으로 사용시 문제가 발생하는데,
애초에 로그인 서비스 테스트에서 MemberRepository 를 목으로 사용하지 않는 것이 더 좋지 않을까 생각되네요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우선 운용하는 @MockBean은 KakaoOauthProvider 하나로 좁혀졌습니다.
MemberRepository, JwtTokenProvider 등, 필요없는 MockBean은 전부 제거하였어요

혹시 @MockBean 들도 추상 클래스에 protected 로 재정의하여 전체 컨텍스트를 2개로 줄이는 건 어떻게 생각하실까요??

그런데 @MockBean을 추상 클래스에 위치시키게 되면 MockBean이 관심사가 아닌 테스트들까지 참조가 열리게 됩니다.

  • TravelogueService는 KakaoOauthProvider 목빈이 필요로 하지 않음에도 참조가 열림

그리고 MockBean을 XXXIntegrationTest에 위치시키는 것 자체가 통합 테스트를 부정하는 어폐가 있다고 생각도 드네요

MockBean을 추상클래스안에 위치시키면 스프링 애플리케이션 컨텍스트를 두개로 운용할 수 있음을 확인했는데요~ 선술한 이유를 근거로 추상클래스안에 위치시키지는 않았습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여담으로 통합테스트의 의미를 극한으로 살리기 위해서 MockBean stubbing이 아닌 MockServer를 운용하는 방식도 생각해보았어요. 이 경우 통합테스트의 의미를 살리면서 애플리케이션 컨텍스트도 캐싱하며 사용할 수 있는데요..! 이는 이번 PR의 관심사는 아닌 듯 하군요. 다음 이슈에서 작업되면 매우 좋을 것 같습니다.

MockServer 궁금하시면 다음 링크 한 번 살펴봐주세요

MockServer 공식 사이트

@LocalServerPort
private int port;

@BeforeEach
void setUp() {
RestAssured.port = port;
databaseCleaner.executeTruncate();
}

@DisplayName("카카오 로그인 요청을 처리할 수 있다")
@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,29 @@
import kr.touroot.authentication.fixture.OauthUserFixture;
import kr.touroot.authentication.infrastructure.JwtTokenProvider;
import kr.touroot.authentication.infrastructure.KakaoOauthProvider;
import kr.touroot.global.IntegrationTest;
import kr.touroot.global.AbstractServiceIntegrationTest;
import kr.touroot.member.domain.Member;
import kr.touroot.member.fixture.MemberFixture;
import kr.touroot.member.repository.MemberRepository;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;

@DisplayName("로그인 서비스")
@ExtendWith(MockitoExtension.class)
class LoginServiceTest extends IntegrationTest {
class LoginServiceTest extends AbstractServiceIntegrationTest {

private static final String AUTHENTICATION_CODE = "test-authentication-code";
private static final String REDIRECT_URI = "http%3A%2F%2Flocalhost%3A8080%2Fapi%2Fv1%2Flogin%2Foauth%2Fkakao";
private static final Member MEMBER = MemberFixture.KAKAO_MEMBER.getMember();

@InjectMocks
@Autowired
private LoginService loginService;
@Mock
@MockBean
private MemberRepository memberRepository;
@Mock
@MockBean
private KakaoOauthProvider kakaoOauthProvider;
@Mock
@MockBean
private JwtTokenProvider jwtTokenProvider;

@DisplayName("투룻 회원가입이 되어 있는 회원의 카카오 소셜 로그인을 처리할 수 있다")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package kr.touroot.global;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.restassured.RestAssured;
import kr.touroot.authentication.infrastructure.JwtTokenProvider;
import kr.touroot.utils.DatabaseCleaner;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.server.LocalServerPort;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public abstract class AbstractControllerIntegrationTest extends AbstractIntegrationTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

매번 불필요하게 반복되던 공통 로직이 잘 모였군요 👍
네이밍은 전 지금도 좋은 것 같습니다. 조금 긴 감이 있긴한데 오히려 명확한게 좋다고 생각해요~

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 이름 좋다고 생각합니다! 실제로 스프링에서 제공해주는 클래스에도 Abstract로 시작하는 클래스들이 많아서 어색하지 않다고 생각되어요!


@Autowired
protected DatabaseCleaner databaseCleaner;
@Autowired
protected ObjectMapper objectMapper;
@Autowired
protected JwtTokenProvider jwtTokenProvider;
@LocalServerPort
protected int port;

@BeforeEach
protected void baseSetUp() {
RestAssured.port = port;
databaseCleaner.executeTruncate();
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package kr.touroot.global;

import java.io.IOException;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.containers.localstack.LocalStackContainer;
import org.testcontainers.containers.localstack.LocalStackContainer.Service;
import org.testcontainers.utility.DockerImageName;

public abstract class IntegrationTest {
@TestPropertySource(properties = {"spring.config.location = classpath:application-test.yml"})
@ActiveProfiles("test")
public abstract class AbstractIntegrationTest {

private static final DockerImageName MYSQL_IMAGE_NAME = DockerImageName.parse("mysql:8");
private static final DockerImageName LOCALSTACK_IMAGE_NAME = DockerImageName.parse("localstack/localstack");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package kr.touroot.global;

import kr.touroot.utils.DatabaseCleaner;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;

@SpringBootTest(webEnvironment = WebEnvironment.NONE)
public abstract class AbstractServiceIntegrationTest extends AbstractIntegrationTest {

@Autowired
protected DatabaseCleaner databaseCleaner;

@BeforeEach
protected void baseSetUp() {
databaseCleaner.executeTruncate();
}
}
15 changes: 0 additions & 15 deletions backend/src/test/java/kr/touroot/global/AcceptanceTest.java

This file was deleted.

22 changes: 0 additions & 22 deletions backend/src/test/java/kr/touroot/global/ServiceTest.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import java.util.List;
import kr.touroot.global.AcceptanceTest;
import kr.touroot.global.IntegrationTest;
import kr.touroot.global.AbstractServiceIntegrationTest;
import kr.touroot.global.exception.S3UploadException;
import kr.touroot.image.domain.ImageFile;
import org.junit.jupiter.api.DisplayName;
Expand All @@ -16,22 +15,14 @@
import org.springframework.web.multipart.MultipartFile;

@DisplayName("Aws S3 프로바이더")
@AcceptanceTest
class AwsS3ProviderTest extends IntegrationTest {

private final AwsS3Provider s3Provider;
private final String temporaryStoragePath;
private final String imageStoragePath;

public AwsS3ProviderTest(
@Autowired AwsS3Provider s3Provider,
@Value("${cloud.aws.s3.temporary-storage-path}") String temporaryStoragePath,
@Value("${cloud.aws.s3.image-storage-path}") String imageStoragePath
) {
this.s3Provider = s3Provider;
this.temporaryStoragePath = temporaryStoragePath;
this.imageStoragePath = imageStoragePath;
}
class AwsS3ProviderTest extends AbstractServiceIntegrationTest {

@Autowired
private AwsS3Provider s3Provider;
@Value("${cloud.aws.s3.temporary-storage-path}")
private String temporaryStoragePath;
@Value("${cloud.aws.s3.image-storage-path}")
private String imageStoragePath;

@DisplayName("유효한 url을 통해 이미지를 영구 폴더로 복사하면 새로운 url을 반환한다.")
@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,16 @@

import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import kr.touroot.global.AcceptanceTest;
import kr.touroot.global.IntegrationTest;
import kr.touroot.global.AbstractControllerIntegrationTest;
import kr.touroot.member.dto.request.MemberRequest;
import kr.touroot.member.fixture.MemberFixture;
import kr.touroot.utils.DatabaseCleaner;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.server.LocalServerPort;

@DisplayName("사용자 컨트롤러")
@AcceptanceTest
class MemberControllerTest extends IntegrationTest {

private final DatabaseCleaner databaseCleaner;

@LocalServerPort
private int port;

@Autowired
public MemberControllerTest(DatabaseCleaner databaseCleaner) {
this.databaseCleaner = databaseCleaner;
}

@BeforeEach
void setUp() {
RestAssured.port = port;

databaseCleaner.executeTruncate();
}
class MemberControllerTest extends AbstractControllerIntegrationTest {

@DisplayName("회원 가입을 한다.")
@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,60 +5,36 @@
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import java.util.List;
import kr.touroot.authentication.infrastructure.JwtTokenProvider;
import kr.touroot.global.AcceptanceTest;
import kr.touroot.global.IntegrationTest;
import kr.touroot.global.AbstractControllerIntegrationTest;
import kr.touroot.image.domain.ImageFile;
import kr.touroot.image.infrastructure.AwsS3Provider;
import kr.touroot.member.domain.Member;
import kr.touroot.member.dto.request.ProfileUpdateRequest;
import kr.touroot.travelogue.helper.TravelogueTestHelper;
import kr.touroot.travelplan.helper.TravelPlanTestHelper;
import kr.touroot.utils.DatabaseCleaner;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

@DisplayName("마이 페이지 컨트롤러")
@AcceptanceTest
class MyPageControllerTest extends IntegrationTest {
class MyPageControllerTest extends AbstractControllerIntegrationTest {

private final DatabaseCleaner databaseCleaner;
private final JwtTokenProvider jwtTokenProvider;
private final TravelogueTestHelper travelogueTestHelper;
private final TravelPlanTestHelper travelPlanTestHelper;
@Autowired
private TravelogueTestHelper travelogueTestHelper;
@Autowired
private TravelPlanTestHelper travelPlanTestHelper;
@Autowired
private AwsS3Provider s3Provider;

@LocalServerPort
private int port;
private String accessToken;
private Member member;
private final AwsS3Provider s3Provider;

@Autowired
public MyPageControllerTest(
DatabaseCleaner databaseCleaner,
JwtTokenProvider jwtTokenProvider,
TravelogueTestHelper travelogueTestHelper,
TravelPlanTestHelper travelPlanTestHelper,
AwsS3Provider s3Provider
) {
this.databaseCleaner = databaseCleaner;
this.jwtTokenProvider = jwtTokenProvider;
this.travelogueTestHelper = travelogueTestHelper;
this.travelPlanTestHelper = travelPlanTestHelper;
this.s3Provider = s3Provider;
}

@BeforeEach
void setUp() {
RestAssured.port = port;
databaseCleaner.executeTruncate();

member = travelogueTestHelper.initKakaoMemberTestData();
accessToken = jwtTokenProvider.createToken(member.getId())
.accessToken();
Expand Down
Loading
Loading