Skip to content

Feat: 테스트별 쿼리 수 상한을 둘 수 있게 한다 - #116

Merged
jjh75607 merged 1 commit into
masterfrom
feat/max-queries-per-test
Aug 19, 2026
Merged

jjh75607 merged 1 commit into
masterfrom
feat/max-queries-per-test

Conversation

@jjh75607

Copy link
Copy Markdown
Owner

Pull Request

Related Issue

  • 없음. 공개 프로젝트 측정에서 근거가 나온 항목이다.

Summary

query-counter.max-queries.per-test한 테스트가 쓸 수 있는 쿼리 수의 상한을 둔다. 테스트에는 아무것도 안 적는다.

Details

어서션과 무엇이 다른가

손으로 적는 어서션 상한
말하는 것 한 테스트의 정확한 수 모든 테스트의 대략의 수
테스트에 적는 것 select(3) 없음
도입 비용 테스트 개수에 비례 설정 한 줄
잡는 것 3에서 4로 3에서 200으로

3에서 12로 조용히 늘어나는 것은 쓸만한 상한 아래에 머무른다. 그건 괜찮다. 상한은 폭주를 막는 그물이고 회귀 검사가 아니다. 반복문 안에서 리포지토리를 부르면 쿼리가 데이터 건수만큼 붙는데, 그것이 어느 테스트에서 일어나든 잡는다.

보고 모드를 함께 둔 이유

상한을 어림수로 잡으면 둘 중 하나가 된다. 지금 통과하는 테스트가 깨지거나, 아무것도 안 걸린다.

query-counter.max-queries.report 로 한 번 돌리면 테스트마다 한 줄씩 나온다. 그 최대값을 보고 정하면 된다.

보고가 조용히 안 보이는 구성이 있었다

실제 프로젝트에 켜 봤더니 한 줄도 안 나왔다. 그 프로젝트 테스트 설정이 logging.level.root: warn 이어서 info 보고가 걸러진 것이다. 흔한 구성이다.

켰는데 아무 일도 없는 것으로 읽히므로, 로거의 info 가 닫혀 있으면 경고로 한 번 알린다.

query-counter.max-queries.report is on but info logging is off for ...QueryLimitWatch,
so nothing will be printed. Set logging.level.soon.springtestutil=info.

한 번만 알리는 표시로 AtomicBoolean 을 쓴다. 테스트 사이에 되돌릴 필요가 없는 값이라 리셋되지 않아 문제가 됐던 예전의 static 플래그와 성질이 다르다. 그 차이를 주석에 적었다.

배선

설정 전달은 N+1 모드와 같은 경로다. AutoConfig 가 읽어 리스너까지 넘기고, 리스너가 기록할 때 ThreadLocal 에 실어 나른다. 리스너가 애플리케이션 컨텍스트를 만지지 않는다는 제약이 그대로 지켜진다.

판정은 어서션과 N+1 다음에 부른다. 그쪽이 더 구체적인 말을 하므로 먼저 실패해야 한다.

확인

실제 프로젝트(Gradle, Boot 4.0.5, 테스트 164개)에서 봤다.

확인 결과
보고 모드 75개 테스트가 보고됨. 최대 27, 중앙 19
수가 안정적인가 같은 스위트를 두 번 돌려 75개 전부 동일. 상한을 걸 수 있는 전제다
상한 20 (측정 최대는 27) 28개가 걸린다. 메시지에 실제 개수와 상한과 종류별 내역이 나온다
상한 50 전부 통과

실패 메시지 예다.

[Test: ...SaleRecordRepositoryIntegrationTest#페이지_크기보다_결과가_많으면...]
Query limit exceeded: 25 queries ran, the limit is 20
  {SELECT=4, INSERT=7, OTHERS=14}

OTHERS=14 가 그 프로젝트의 테이블 비우기 리스너가 날리는 것이다. 테스트 준비 과정의 쿼리도 함께 세진다. 상한을 정할 때 알아야 하는 사실이라 README 두 벌에 적었다.

로컬에서 지원 범위 셋(3.0.0, 3.5.16, 4.1.0)을 모두 빌드했다.

테스트

열 개를 더했다.

무엇 개수
값 객체: 꺼짐 판정, 보고만 켠 경우, 상한 경계, 음수 4
검사: 꺼짐, 초과, 경계값, 보고, 보고와 상한 함께, 로그가 닫힌 경우 5
프로퍼티에서 테스트 경계까지 전달되는지 1

손으로 적는 어서션은 한 테스트의 정확한 수를 말한다. 그래서 도입 비용이 테스트 개수에
비례하고, 테스트가 이미 수백 개면 못 들어간다. 상한은 **모든 테스트에 대해 대략의 수**를
말하고 어느 테스트에도 아무것도 안 적는다.

`query-counter.max-queries.per-test` 다. 잡는 것이 어서션과 다르다. 반복문 안에서 리포지토리를
부르면 쿼리 3개가 200개가 되는데 그것이 어느 테스트에서 일어나든 잡는다. 3개가 12개로 조용히
늘어나는 것은 쓸만한 상한 아래에 머무른다. 그건 괜찮다 — 상한은 폭주를 막는 그물이고 회귀
검사가 아니다.

`query-counter.max-queries.report` 를 함께 둔 이유가 있다. 상한을 어림수로 잡으면 지금 통과하는
테스트가 깨지거나 아무것도 안 걸린다. 보고 모드로 한 번 돌려 최대값을 보고 정하는 것이 맞다.

**보고가 조용히 안 보이는 구성이 있다.** 테스트 설정에서 `logging.level.root: warn` 을 두는
프로젝트가 흔하고 그러면 info 보고가 걸러진다. 실제 프로젝트에 켜 봤더니 그렇게 됐다. 켰는데
아무 일도 없는 것으로 읽히므로, 로거의 info 가 닫혀 있으면 경고로 한 번 알린다.

설정 전달은 N+1 모드와 같은 경로다. AutoConfig 가 읽어 리스너까지 넘기고 리스너가 ThreadLocal
에 실어 나른다. 판정은 어서션과 N+1 다음에 부른다. 그쪽이 더 구체적인 말을 하므로 먼저 실패해야
한다.

실제 프로젝트(테스트 164개)에서 확인했다. 보고 모드에서 최대 27, 중앙 19 가 나왔고, 같은 스위트를
두 번 돌려 75개 테스트의 수가 하나도 다르지 않은 것도 확인했다. 상한 20 으로 두면 28개가 걸리고
50 으로 두면 전부 통과한다. 테스트 준비 과정의 TRUNCATE 같은 쿼리도 함께 세지므로 그 사실을
README 두 벌에 적었다.

테스트 열 개를 더했다. 지원 버전 셋을 로컬에서 확인했다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jjh75607
jjh75607 merged commit 93701f4 into master Aug 19, 2026
6 checks passed
@jjh75607
jjh75607 deleted the feat/max-queries-per-test branch August 19, 2026 06:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant