개발이군고구마

[사면초가 TDD/OOP] 3. 통합테스트 (config mock의 주입) 본문

SERVER/Architecture

[사면초가 TDD/OOP] 3. 통합테스트 (config mock의 주입)

김구황 2025. 6. 8. 20:08
728x90

 

❇ 현재 진행단계. 

[OOP 설계 프로세스]
 1. 설계 원칙 확립 < 구현 1단계
2. TDD 구현 단위 < 구현 2단계
3. TDD 구현 통합
4. 이터레이션과 현실

 


 

1. 서비스 통합 테스트

🌐 config mock을 어떻게 만들어 실제 테스트에 주입시킬 것인가

 

 

1) 테스트가 실행되는데 세팅되어야할 config 파일 bean으로 spring container에 담아주기 

@SpringBootTest(classes = {
    GreenApplication.class,
    FileTestUtil.class,
    MySqlTestContainerConfig.class,
    S3TestContainerConfig.class
})
public class ServiceIntegrationTest {
}

 

 

a. configuration 

설정 환경 빈으로 등록 

 

  • MySqlContainer 객체 -> DB 와 연동함 
@TestConfiguration
public class MySqlTestContainerConfig {

    @Bean
    @ServiceConnection
    public MySQLContainer<?> mySQLContainer() {
       return new MySQLContainer<>("mysql:8.0");
    }
}

 

  • S3Client 객체 -> AWS 와 연동함 
@TestConfiguration
public class S3TestContainerConfig {

    @Bean(initMethod = "start", destroyMethod = "stop")
    public LocalStackContainer localStackContainer() {
       DockerImageName dockerImageName = DockerImageName.parse("localstack/localstack:3.0");
       LocalStackContainer localStackContainer = new LocalStackContainer(dockerImageName);
       localStackContainer.withServices(LocalStackContainer.Service.S3);
       return localStackContainer;
    }

    @Bean
    public S3Client testS3Client(LocalStackContainer localStackContainer) {
       return S3Client.builder()
          .endpointOverride(localStackContainer.getEndpointOverride(LocalStackContainer.Service.S3))
          .credentialsProvider(StaticCredentialsProvider.create(
             AwsBasicCredentials.create("test", "test")
          ))
          .region(Region.of(localStackContainer.getRegion()))
          .build();
    }
}

 

 

 

b. component

의존성 주입을 시켜서 메서드들을 이용할 클래스 분리해놓음

▶ 테스트 코드 작성시 긴 메서드를 축약시킬 수 있음 

@Component
public class FileTestUtil {

    @Autowired
    private S3StorageHelper storageHelper;
    @Autowired
    private S3Properties s3Properties;
    @Autowired
    private S3Client s3Client;
    @Autowired
    private FileJpaRepository fileJpaRepository;

    public boolean existsImageInS3(String imageUrl) {
       String imageKey = storageHelper.extractImageKey(imageUrl);
       try {
          s3Client.headObject(HeadObjectRequest.builder()
             .bucket(s3Properties.getBucket())
             .key(imageKey)
             .build());
          return true;
       } catch (NoSuchKeyException e) {
          return false;
       }
    }
    ....등등
}

 

 


 

 

2) config를 상속받은 진짜 테스트 클래스  

  • 성공하는 경우 DB 에 잘 저장되었는지
  • 실패하는 경우 예외를 잘 반환하였는지
public class FileServiceIntTest extends ServiceIntegrationTest {

	@Autowired
	private FileService fileService;
	@Autowired
	private FileTestUtil fileTestUtil;

	@BeforeEach
	void setUp() {
		fileTestUtil.createBucket();
	}

	@AfterEach
	void tearDown() {
		fileTestUtil.clearBucket();
	}

	// 성공 
	@Test
	void 이미지_파일을_업로드하면_db와_storage에_저장된다() {
		// given
		MultipartFile imageFile = createMockFile("image1.jpg", "image/jpeg");

		// when
		String imageUrl = fileService.uploadImage(imageFile, Purpose.PROFILE);

		// then
		assertThat(fileTestUtil.existsImageInS3(imageUrl)).isTrue();
		assertThat(fileTestUtil.existsInImageMetadataInDb(imageUrl)).isTrue();
	}
    
    // 실패
	@Test
	void 이미지_업로드_중_예외가_발생하면_db에_저장된_파일이_롤백된다() {
		// given
		MultipartFile imageFile = createMockFile("image.png", "image/png");
		fileTestUtil.clearBucket();

		// when & then
		assertThatThrownBy(() -> fileService.uploadImage(imageFile, Purpose.PROFILE))
			.isInstanceOf(FileException.class)
			.hasFieldOrPropertyWithValue("exceptionMessage", FileExceptionMessage.IMAGE_UPLOAD_FAILED);

		assertThat(fileTestUtil.countInImageMetadataInDb()).isZero();
	}

	private static MultipartFile createMockFile(String originalName, String contentType) {
		return new MockMultipartFile("image", originalName, contentType, "imageFile".getBytes());
	}
}
  •  DB 연동 부분 : fileJpaRepository.save(fileEntity);
  • AWS 연동 부분 : s3Client.putObject(request, RequestBody.fromInputStream(inputStream, imageFile.getSize()));