Skip to content

Commit

Permalink
[Feature] - BE 테스트 개선 스프링 컨텍스트 캐싱 최적화 (#625)
Browse files Browse the repository at this point in the history
* refactor: 모든 서비스 테스트 스프링 부트 테스트 기반으로 통일 개

* refactor: IntegerationTest -> AbstractIntegrationTest 이름 변경

* refactor: 컨트롤러 테스트 구조 계층화

* refactor: 테스트 공통 로직 메서드명 변경

* refactor: 컨트롤러 테스트가 공통 로직을 사용하도록 추상화된 클래스 상속

* feat: 서비스 통합 테스트 추상 클래스 정의

* feat: 서비스 통합 테스트들이 추상화된 공통로직을 사용하도록 상속

* refactor: 사용하지 않는 애너테이션 제거 개선

* refactor: 불필요한 MockBean 제거 (MemberRepository, JwtTokenProvider)

* refactor: MockBean 속성 접근 제어 수준 강화 (protected -> private)
  • Loading branch information
Libienz authored Dec 30, 2024
1 parent 7131158 commit 8a65589
Show file tree
Hide file tree
Showing 22 changed files with 165 additions and 510 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public LoginResponse login(String code, String encodedRedirectUri) {
return LoginResponse.of(member, tokenProvider.createToken(member.getId()));
}

private Member signUp(OauthUserInformationResponse userInformation) {
public Member signUp(OauthUserInformationResponse userInformation) {
return memberRepository.save(userInformation.toMember());
}

Expand Down
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;
@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 @@ -2,88 +2,59 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.Optional;
import kr.touroot.authentication.dto.response.LoginResponse;
import kr.touroot.authentication.dto.response.TokenResponse;
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
@Autowired
private MemberRepository memberRepository;
@Mock
private KakaoOauthProvider kakaoOauthProvider;
@Mock
private JwtTokenProvider jwtTokenProvider;
@MockBean
private KakaoOauthProvider oauthProvider;

@DisplayName("투룻 회원가입이 되어 있는 회원의 카카오 소셜 로그인을 처리할 수 있다")
@Test
void existUserKakaoSocialLoginTest() {
// given
String accessToken = "aaa";
String refreshToken = "bbb";

when(kakaoOauthProvider.getUserInformation(any(String.class), any(String.class)))
.thenReturn(OauthUserFixture.KAKAO_USER.getOauthInformationResponse());
when(memberRepository.findByKakaoId(any(Long.class)))
.thenReturn(Optional.of(MEMBER));
when(jwtTokenProvider.createToken(MEMBER.getId()))
.thenReturn(new TokenResponse(accessToken, refreshToken));
OauthUserFixture kakaoUser = OauthUserFixture.KAKAO_USER;
Member preSignedUpUser = loginService.signUp(kakaoUser.getOauthInformationResponse());
when(oauthProvider.getUserInformation(any(String.class), any(String.class)))
.thenReturn(kakaoUser.getOauthInformationResponse());

// when
LoginResponse response = loginService.login(AUTHENTICATION_CODE, REDIRECT_URI);

// when & then
assertThat(response).isEqualTo(
LoginResponse.of(MEMBER, new TokenResponse(response.accessToken(), response.refreshToken()))
);
// then
assertThat(response.memberId()).isEqualTo(preSignedUpUser.getId());
}

@DisplayName("투룻 회원가입이 되어 있지 않은 회원은 소셜 로그인 과정에서 회원가입 후 로그인 된다")
@Test
void nonExistUserKakaoSocialLoginTest() {
// given
String accessToken = "aaa";
String refreshToken = "bbb";

when(kakaoOauthProvider.getUserInformation(any(String.class), any(String.class)))
when(oauthProvider.getUserInformation(any(String.class), any(String.class)))
.thenReturn(OauthUserFixture.KAKAO_USER.getOauthInformationResponse());
when(memberRepository.findByKakaoId(any(Long.class)))
.thenReturn(Optional.empty());
when(memberRepository.save(any(Member.class)))
.thenReturn(MEMBER);
when(jwtTokenProvider.createToken(MEMBER.getId()))
.thenReturn(new TokenResponse(accessToken, refreshToken));

// when
LoginResponse response = loginService.login(AUTHENTICATION_CODE, REDIRECT_URI);

// when & then
assertThat(response).isEqualTo(
LoginResponse.of(MEMBER, new TokenResponse(response.accessToken(), response.refreshToken()))
);
verify(memberRepository, times(1)).save(any(Member.class));
// then
assertThat(response.memberId()).isNotNull();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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 {

@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
Loading

0 comments on commit 8a65589

Please sign in to comment.