Skip to content

Commit 8a65589

Browse files
authored
[Feature] - BE 테스트 개선 스프링 컨텍스트 캐싱 최적화 (#625)
* refactor: 모든 서비스 테스트 스프링 부트 테스트 기반으로 통일 개 * refactor: IntegerationTest -> AbstractIntegrationTest 이름 변경 * refactor: 컨트롤러 테스트 구조 계층화 * refactor: 테스트 공통 로직 메서드명 변경 * refactor: 컨트롤러 테스트가 공통 로직을 사용하도록 추상화된 클래스 상속 * feat: 서비스 통합 테스트 추상 클래스 정의 * feat: 서비스 통합 테스트들이 추상화된 공통로직을 사용하도록 상속 * refactor: 사용하지 않는 애너테이션 제거 개선 * refactor: 불필요한 MockBean 제거 (MemberRepository, JwtTokenProvider) * refactor: MockBean 속성 접근 제어 수준 강화 (protected -> private)
1 parent 7131158 commit 8a65589

22 files changed

+165
-510
lines changed

backend/src/main/java/kr/touroot/authentication/service/LoginService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public LoginResponse login(String code, String encodedRedirectUri) {
3434
return LoginResponse.of(member, tokenProvider.createToken(member.getId()));
3535
}
3636

37-
private Member signUp(OauthUserInformationResponse userInformation) {
37+
public Member signUp(OauthUserInformationResponse userInformation) {
3838
return memberRepository.save(userInformation.toMember());
3939
}
4040

backend/src/test/java/kr/touroot/authentication/controller/LoginControllerTest.java

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,39 +12,21 @@
1212
import kr.touroot.authentication.dto.response.TokenResponse;
1313
import kr.touroot.authentication.fixture.OauthUserFixture;
1414
import kr.touroot.authentication.helper.LoginTestHelper;
15-
import kr.touroot.authentication.infrastructure.JwtTokenProvider;
1615
import kr.touroot.authentication.infrastructure.KakaoOauthProvider;
17-
import kr.touroot.global.AcceptanceTest;
18-
import kr.touroot.global.IntegrationTest;
16+
import kr.touroot.global.AbstractControllerIntegrationTest;
1917
import kr.touroot.member.domain.Member;
20-
import kr.touroot.utils.DatabaseCleaner;
21-
import org.junit.jupiter.api.BeforeEach;
2218
import org.junit.jupiter.api.DisplayName;
2319
import org.junit.jupiter.api.Test;
2420
import org.springframework.beans.factory.annotation.Autowired;
2521
import org.springframework.boot.test.mock.mockito.MockBean;
26-
import org.springframework.boot.test.web.server.LocalServerPort;
2722

2823
@DisplayName("로그인 컨트롤러")
29-
@AcceptanceTest
30-
class LoginControllerTest extends IntegrationTest {
24+
class LoginControllerTest extends AbstractControllerIntegrationTest {
3125

32-
@Autowired
33-
private JwtTokenProvider jwtTokenProvider;
34-
@Autowired
35-
private DatabaseCleaner databaseCleaner;
3626
@Autowired
3727
private LoginTestHelper testHelper;
3828
@MockBean
3929
private KakaoOauthProvider oauthProvider;
40-
@LocalServerPort
41-
private int port;
42-
43-
@BeforeEach
44-
void setUp() {
45-
RestAssured.port = port;
46-
databaseCleaner.executeTruncate();
47-
}
4830

4931
@DisplayName("카카오 로그인 요청을 처리할 수 있다")
5032
@Test

backend/src/test/java/kr/touroot/authentication/service/LoginServiceTest.java

Lines changed: 19 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,88 +2,59 @@
22

33
import static org.assertj.core.api.Assertions.assertThat;
44
import static org.mockito.ArgumentMatchers.any;
5-
import static org.mockito.Mockito.times;
6-
import static org.mockito.Mockito.verify;
75
import static org.mockito.Mockito.when;
86

9-
import java.util.Optional;
107
import kr.touroot.authentication.dto.response.LoginResponse;
11-
import kr.touroot.authentication.dto.response.TokenResponse;
128
import kr.touroot.authentication.fixture.OauthUserFixture;
13-
import kr.touroot.authentication.infrastructure.JwtTokenProvider;
149
import kr.touroot.authentication.infrastructure.KakaoOauthProvider;
15-
import kr.touroot.global.IntegrationTest;
10+
import kr.touroot.global.AbstractServiceIntegrationTest;
1611
import kr.touroot.member.domain.Member;
17-
import kr.touroot.member.fixture.MemberFixture;
1812
import kr.touroot.member.repository.MemberRepository;
1913
import org.junit.jupiter.api.DisplayName;
2014
import org.junit.jupiter.api.Test;
21-
import org.junit.jupiter.api.extension.ExtendWith;
22-
import org.mockito.InjectMocks;
23-
import org.mockito.Mock;
24-
import org.mockito.junit.jupiter.MockitoExtension;
15+
import org.springframework.beans.factory.annotation.Autowired;
16+
import org.springframework.boot.test.mock.mockito.MockBean;
2517

2618
@DisplayName("로그인 서비스")
27-
@ExtendWith(MockitoExtension.class)
28-
class LoginServiceTest extends IntegrationTest {
19+
class LoginServiceTest extends AbstractServiceIntegrationTest {
2920

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

34-
@InjectMocks
24+
@Autowired
3525
private LoginService loginService;
36-
@Mock
26+
@Autowired
3727
private MemberRepository memberRepository;
38-
@Mock
39-
private KakaoOauthProvider kakaoOauthProvider;
40-
@Mock
41-
private JwtTokenProvider jwtTokenProvider;
28+
@MockBean
29+
private KakaoOauthProvider oauthProvider;
4230

4331
@DisplayName("투룻 회원가입이 되어 있는 회원의 카카오 소셜 로그인을 처리할 수 있다")
4432
@Test
4533
void existUserKakaoSocialLoginTest() {
4634
// given
47-
String accessToken = "aaa";
48-
String refreshToken = "bbb";
49-
50-
when(kakaoOauthProvider.getUserInformation(any(String.class), any(String.class)))
51-
.thenReturn(OauthUserFixture.KAKAO_USER.getOauthInformationResponse());
52-
when(memberRepository.findByKakaoId(any(Long.class)))
53-
.thenReturn(Optional.of(MEMBER));
54-
when(jwtTokenProvider.createToken(MEMBER.getId()))
55-
.thenReturn(new TokenResponse(accessToken, refreshToken));
35+
OauthUserFixture kakaoUser = OauthUserFixture.KAKAO_USER;
36+
Member preSignedUpUser = loginService.signUp(kakaoUser.getOauthInformationResponse());
37+
when(oauthProvider.getUserInformation(any(String.class), any(String.class)))
38+
.thenReturn(kakaoUser.getOauthInformationResponse());
5639

40+
// when
5741
LoginResponse response = loginService.login(AUTHENTICATION_CODE, REDIRECT_URI);
5842

59-
// when & then
60-
assertThat(response).isEqualTo(
61-
LoginResponse.of(MEMBER, new TokenResponse(response.accessToken(), response.refreshToken()))
62-
);
43+
// then
44+
assertThat(response.memberId()).isEqualTo(preSignedUpUser.getId());
6345
}
6446

6547
@DisplayName("투룻 회원가입이 되어 있지 않은 회원은 소셜 로그인 과정에서 회원가입 후 로그인 된다")
6648
@Test
6749
void nonExistUserKakaoSocialLoginTest() {
6850
// given
69-
String accessToken = "aaa";
70-
String refreshToken = "bbb";
71-
72-
when(kakaoOauthProvider.getUserInformation(any(String.class), any(String.class)))
51+
when(oauthProvider.getUserInformation(any(String.class), any(String.class)))
7352
.thenReturn(OauthUserFixture.KAKAO_USER.getOauthInformationResponse());
74-
when(memberRepository.findByKakaoId(any(Long.class)))
75-
.thenReturn(Optional.empty());
76-
when(memberRepository.save(any(Member.class)))
77-
.thenReturn(MEMBER);
78-
when(jwtTokenProvider.createToken(MEMBER.getId()))
79-
.thenReturn(new TokenResponse(accessToken, refreshToken));
8053

54+
// when
8155
LoginResponse response = loginService.login(AUTHENTICATION_CODE, REDIRECT_URI);
8256

83-
// when & then
84-
assertThat(response).isEqualTo(
85-
LoginResponse.of(MEMBER, new TokenResponse(response.accessToken(), response.refreshToken()))
86-
);
87-
verify(memberRepository, times(1)).save(any(Member.class));
57+
// then
58+
assertThat(response.memberId()).isNotNull();
8859
}
8960
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package kr.touroot.global;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import io.restassured.RestAssured;
5+
import kr.touroot.authentication.infrastructure.JwtTokenProvider;
6+
import kr.touroot.utils.DatabaseCleaner;
7+
import org.junit.jupiter.api.BeforeEach;
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.boot.test.context.SpringBootTest;
10+
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
11+
import org.springframework.boot.test.web.server.LocalServerPort;
12+
13+
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
14+
public abstract class AbstractControllerIntegrationTest extends AbstractIntegrationTest {
15+
16+
@Autowired
17+
protected DatabaseCleaner databaseCleaner;
18+
@Autowired
19+
protected ObjectMapper objectMapper;
20+
@Autowired
21+
protected JwtTokenProvider jwtTokenProvider;
22+
23+
@LocalServerPort
24+
protected int port;
25+
26+
@BeforeEach
27+
protected void baseSetUp() {
28+
RestAssured.port = port;
29+
databaseCleaner.executeTruncate();
30+
}
31+
}

backend/src/test/java/kr/touroot/global/IntegrationTest.java renamed to backend/src/test/java/kr/touroot/global/AbstractIntegrationTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package kr.touroot.global;
22

33
import java.io.IOException;
4+
import org.springframework.test.context.ActiveProfiles;
45
import org.springframework.test.context.DynamicPropertyRegistry;
56
import org.springframework.test.context.DynamicPropertySource;
7+
import org.springframework.test.context.TestPropertySource;
68
import org.testcontainers.containers.MySQLContainer;
79
import org.testcontainers.containers.localstack.LocalStackContainer;
810
import org.testcontainers.containers.localstack.LocalStackContainer.Service;
911
import org.testcontainers.utility.DockerImageName;
1012

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

1317
private static final DockerImageName MYSQL_IMAGE_NAME = DockerImageName.parse("mysql:8");
1418
private static final DockerImageName LOCALSTACK_IMAGE_NAME = DockerImageName.parse("localstack/localstack");
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package kr.touroot.global;
2+
3+
import kr.touroot.utils.DatabaseCleaner;
4+
import org.junit.jupiter.api.BeforeEach;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.boot.test.context.SpringBootTest;
7+
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
8+
9+
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
10+
public abstract class AbstractServiceIntegrationTest extends AbstractIntegrationTest {
11+
12+
@Autowired
13+
protected DatabaseCleaner databaseCleaner;
14+
15+
@BeforeEach
16+
protected void baseSetUp() {
17+
databaseCleaner.executeTruncate();
18+
}
19+
}

backend/src/test/java/kr/touroot/global/AcceptanceTest.java

Lines changed: 0 additions & 15 deletions
This file was deleted.

backend/src/test/java/kr/touroot/global/ServiceTest.java

Lines changed: 0 additions & 22 deletions
This file was deleted.

backend/src/test/java/kr/touroot/image/infrastructure/AwsS3ProviderTest.java

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
import static org.assertj.core.api.Assertions.assertThatThrownBy;
55

66
import java.util.List;
7-
import kr.touroot.global.AcceptanceTest;
8-
import kr.touroot.global.IntegrationTest;
7+
import kr.touroot.global.AbstractServiceIntegrationTest;
98
import kr.touroot.global.exception.S3UploadException;
109
import kr.touroot.image.domain.ImageFile;
1110
import org.junit.jupiter.api.DisplayName;
@@ -16,22 +15,14 @@
1615
import org.springframework.web.multipart.MultipartFile;
1716

1817
@DisplayName("Aws S3 프로바이더")
19-
@AcceptanceTest
20-
class AwsS3ProviderTest extends IntegrationTest {
21-
22-
private final AwsS3Provider s3Provider;
23-
private final String temporaryStoragePath;
24-
private final String imageStoragePath;
25-
26-
public AwsS3ProviderTest(
27-
@Autowired AwsS3Provider s3Provider,
28-
@Value("${cloud.aws.s3.temporary-storage-path}") String temporaryStoragePath,
29-
@Value("${cloud.aws.s3.image-storage-path}") String imageStoragePath
30-
) {
31-
this.s3Provider = s3Provider;
32-
this.temporaryStoragePath = temporaryStoragePath;
33-
this.imageStoragePath = imageStoragePath;
34-
}
18+
class AwsS3ProviderTest extends AbstractServiceIntegrationTest {
19+
20+
@Autowired
21+
private AwsS3Provider s3Provider;
22+
@Value("${cloud.aws.s3.temporary-storage-path}")
23+
private String temporaryStoragePath;
24+
@Value("${cloud.aws.s3.image-storage-path}")
25+
private String imageStoragePath;
3526

3627
@DisplayName("유효한 url을 통해 이미지를 영구 폴더로 복사하면 새로운 url을 반환한다.")
3728
@Test

backend/src/test/java/kr/touroot/member/controller/MemberControllerTest.java

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,16 @@
44

55
import io.restassured.RestAssured;
66
import io.restassured.http.ContentType;
7-
import kr.touroot.global.AcceptanceTest;
8-
import kr.touroot.global.IntegrationTest;
7+
import kr.touroot.global.AbstractControllerIntegrationTest;
98
import kr.touroot.member.dto.request.MemberRequest;
109
import kr.touroot.member.fixture.MemberFixture;
11-
import kr.touroot.utils.DatabaseCleaner;
12-
import org.junit.jupiter.api.BeforeEach;
1310
import org.junit.jupiter.api.DisplayName;
1411
import org.junit.jupiter.api.Test;
1512
import org.junit.jupiter.params.ParameterizedTest;
1613
import org.junit.jupiter.params.provider.ValueSource;
17-
import org.springframework.beans.factory.annotation.Autowired;
18-
import org.springframework.boot.test.web.server.LocalServerPort;
1914

2015
@DisplayName("사용자 컨트롤러")
21-
@AcceptanceTest
22-
class MemberControllerTest extends IntegrationTest {
23-
24-
private final DatabaseCleaner databaseCleaner;
25-
26-
@LocalServerPort
27-
private int port;
28-
29-
@Autowired
30-
public MemberControllerTest(DatabaseCleaner databaseCleaner) {
31-
this.databaseCleaner = databaseCleaner;
32-
}
33-
34-
@BeforeEach
35-
void setUp() {
36-
RestAssured.port = port;
37-
38-
databaseCleaner.executeTruncate();
39-
}
16+
class MemberControllerTest extends AbstractControllerIntegrationTest {
4017

4118
@DisplayName("회원 가입을 한다.")
4219
@Test

0 commit comments

Comments
 (0)