From fcddfea4f65a04b2bae27ee374eaf81d8c2ff720 Mon Sep 17 00:00:00 2001 From: GA Pro Date: Fri, 29 May 2026 23:08:11 +0900 Subject: [PATCH] =?UTF-8?q?test:=20SqlSessionFactory=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=99=98=EA=B2=BD=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20=E2=80=94=20=EC=A0=84=EC=B2=B4=20112=EA=B1=B4=20PAS?= =?UTF-8?q?S?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @SpringBootTest 컨텍스트가 @MapperScan(com.ga) 으로 모든 매퍼 MapperFactoryBean 을 만드는데 test 프로파일이 MybatisAutoConfiguration 을 제외해 SqlSessionFactory 가 없어 깨지던 문제. 기존엔 매퍼를 하나씩 @MockBean 으로 막았으나 신규 매퍼 누락 시마다 컨텍스트 로딩 실패. - TestMyBatisConfig: 미접속 PGSimpleDataSource + SqlSessionFactory 제공 → 전 매퍼 빈 생성, 목록 갱신 불필요 - AbstractControllerTest @Import 에 TestMyBatisConfig 추가 - SettleControllerTest hold stub 2인자→3인자 정정(testCompile 실패 해소) - HANDOFF §3-17 추가 + 테스트 상태 서술 정정 검증: ./gradlew test --rerun-tasks → 112건 PASS (ga-common 57 / ga-api 36 / ga-batch 19, 실패 0). DB 무관. Co-Authored-By: Claude Opus 4.8 (1M context) --- HANDOFF.md | 21 ++++++++- .../settle/SettleControllerTest.java | 2 +- .../api/support/AbstractControllerTest.java | 2 +- .../com/ga/api/support/TestMyBatisConfig.java | 45 +++++++++++++++++++ 4 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 ga-api/src/test/java/com/ga/api/support/TestMyBatisConfig.java diff --git a/HANDOFF.md b/HANDOFF.md index 951d03e..8372d7a 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -380,7 +380,7 @@ P5 전체 + P6 PoC + **P6 후속(2026-05-27)** 완료. 잔여는 외부 SDK 차 운영 DB `192.168.0.60:55432/trading_ai/ga` 현재 **V85 적용 완료** (변경 없음). ga-api 8082 / ga-frontend 3000 / ga-mobile-pwa 3001 모두 라이브. -단위/통합 테스트는 SqlSessionFactory 환경 설정 문제로 아직 미작성 상태입니다. +단위/통합 테스트: **SqlSessionFactory 환경 문제 해결 완료 (2026-05-29, §3-17)**. `./gradlew test` → 112건 전부 PASS (ga-common 57 / ga-api 36 / ga-batch 19). ## 3-15. P6 후속 (2026-05-27, 커밋 `2c9bf71` + `dfce9e5` + 후속 1) @@ -444,6 +444,23 @@ ga-api 8082 / ga-frontend 3000 / ga-mobile-pwa 3001 모두 라이브. - e2e 테스트 DB 부산물(승계 레코드 / 202605 정산데이터 / 회계분개 / 차감 전이 / batch_job_log)은 단일 트랜잭션으로 전부 롤백 완료, pre-e2e 상태 복원 검증. - **주의**: AccountingEntryGenerateStep(Step8b)은 멱등이 아님 — 동일 월 재실행 시 분개 중복 INSERT(주석상 existsByPaymentId 중복방어 언급되나 코드 미구현). 동일 월 재정산 전 기존 분개 정리 필요. +## 3-17. 테스트 환경 — SqlSessionFactory 문제 해결 (2026-05-29, 완료) + +증상: `./gradlew :ga-api:test` 시 Spring 컨텍스트 로딩 실패 — `Error creating bean 'externalCallLogMapper' ... Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required`. + +원인: +- `application-test.yml` 이 MybatisAutoConfiguration / DataSourceAutoConfiguration 을 제외 → 컨텍스트에 SqlSessionFactory 없음. +- 그러나 `MyBatisConfig` 의 `@MapperScan(basePackages = "com.ga")` 가 모든 @Mapper 의 MapperFactoryBean 을 생성, 이들은 SqlSessionFactory 를 요구. +- `AbstractControllerTest` 는 매퍼를 하나씩 `@MockBean` 으로 막아 회피했으나, 이후 추가된 매퍼(ExternalCallLog/Succession/AccountingEntry 등)가 목록에 누락 → 실제 MapperFactoryBean 생성 시도 → 깨짐. **매퍼 추가될 때마다 목록 갱신해야 하는 취약 구조.** +- (부수) `SettleControllerTest` 가 구버전 `hold(id, reason)` 2인자 stub 사용 → 현재 `hold(id, reason, reasonCode)` 3인자와 불일치로 testCompile 실패. + +해결: +- `TestMyBatisConfig`(@TestConfiguration) 신규 — 미접속 `PGSimpleDataSource` + `SqlSessionFactory` 한 쌍 제공. 모든 MapperFactoryBean 이 생성되므로 매퍼 목록 갱신 불필요. 컨트롤러 테스트는 Service 를 mock 하므로 실제 쿼리 미실행(연결 안 함). +- `AbstractControllerTest` `@Import` 에 `TestMyBatisConfig` 추가. +- `SettleControllerTest` hold stub 3인자로 정정. + +검증: `./gradlew test --rerun-tasks` → **BUILD SUCCESSFUL, 112건 PASS** (ga-common 57 / ga-api 36 / ga-batch 19, 실패·에러 0). DB 무관(미접속 DataSource). + ## 3-9. P5-W3 메뉴/권한 전수 정합 보강 (V74 — 완료, 2026-05-21 라이브 적용) | 항목 | 상태 | @@ -513,7 +530,7 @@ ga-api 8082 / ga-frontend 3000 / ga-mobile-pwa 3001 모두 라이브. - **자동 정책**: - 각 단계 완료 후 자동 커밋 (push 안 함) - 빌드/테스트 실패 시 담당 팀원에 재지시 - - 컴파일은 PL이 직접 검증, 테스트는 환경 문제(SqlSessionFactory)로 통과 안 되는 게 알려진 상태 + - 컴파일은 PL이 직접 검증. 테스트는 SqlSessionFactory 환경 문제 해결 후 `./gradlew test` 112건 PASS (§3-17) ## 7. 표준 가정 (도메인 작업 진행 시 채택한 값) diff --git a/ga-api/src/test/java/com/ga/api/controller/settle/SettleControllerTest.java b/ga-api/src/test/java/com/ga/api/controller/settle/SettleControllerTest.java index 1e97964..d888bcc 100644 --- a/ga-api/src/test/java/com/ga/api/controller/settle/SettleControllerTest.java +++ b/ga-api/src/test/java/com/ga/api/controller/settle/SettleControllerTest.java @@ -102,7 +102,7 @@ class SettleControllerTest extends AbstractControllerTest { @Test @DisplayName("PUT /api/settle/{id}/hold - 200 보류 처리") void hold_ok() throws Exception { - willDoNothing().given(service).hold(eq(50L), anyString()); + willDoNothing().given(service).hold(eq(50L), anyString(), any()); mockMvc.perform(put("/api/settle/50/hold") .with(MockLoginUser.admin()) diff --git a/ga-api/src/test/java/com/ga/api/support/AbstractControllerTest.java b/ga-api/src/test/java/com/ga/api/support/AbstractControllerTest.java index 0eb87a8..df7a105 100644 --- a/ga-api/src/test/java/com/ga/api/support/AbstractControllerTest.java +++ b/ga-api/src/test/java/com/ga/api/support/AbstractControllerTest.java @@ -62,7 +62,7 @@ import static org.mockito.Mockito.doAnswer; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) @AutoConfigureMockMvc @ActiveProfiles("test") -@Import(TestSecurityConfig.class) +@Import({TestSecurityConfig.class, TestMyBatisConfig.class}) public abstract class AbstractControllerTest { @Autowired protected MockMvc mockMvc; diff --git a/ga-api/src/test/java/com/ga/api/support/TestMyBatisConfig.java b/ga-api/src/test/java/com/ga/api/support/TestMyBatisConfig.java new file mode 100644 index 0000000..a8bf9a5 --- /dev/null +++ b/ga-api/src/test/java/com/ga/api/support/TestMyBatisConfig.java @@ -0,0 +1,45 @@ +package com.ga.api.support; + +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.postgresql.ds.PGSimpleDataSource; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; + +import javax.sql.DataSource; + +/** + * 테스트용 MyBatis 인프라. + * + * application-test.yml 이 MybatisAutoConfiguration / DataSourceAutoConfiguration 을 제외하므로 + * 컨텍스트에 SqlSessionFactory 가 없다. 그런데 MyBatisConfig 의 + * {@code @MapperScan(basePackages = "com.ga")} 가 모든 @Mapper 인터페이스의 MapperFactoryBean 을 + * 생성하고, 이들은 SqlSessionFactory(또는 SqlSessionTemplate)를 요구한다. + * + * 과거에는 AbstractControllerTest 가 매퍼를 하나씩 @MockBean 으로 막아 이 요구를 회피했으나, + * 새 매퍼가 추가될 때마다 목록을 갱신해야 했고 누락 시 + * "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required" 로 컨텍스트 로딩이 깨졌다. + * + * 여기서 미접속 DataSource 기반 SqlSessionFactory 를 한 번 제공하면 매퍼 빈이 모두 생성되어 + * 그 문제가 사라진다. 컨트롤러 테스트는 Service 를 mock 하므로 실제 쿼리는 실행되지 않는다. + */ +@TestConfiguration +public class TestMyBatisConfig { + + /** 실제 연결하지 않는다. SqlSessionFactory 생성과 Mapper 빈 등록 용도로만 존재. */ + @Bean + public DataSource dataSource() { + PGSimpleDataSource ds = new PGSimpleDataSource(); + ds.setUrl("jdbc:postgresql://localhost:5432/test"); + ds.setUser("test"); + ds.setPassword("test"); + return ds; + } + + @Bean + public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { + SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); + factory.setDataSource(dataSource); + return factory.getObject(); + } +}