Hibernate를 통해 실행되는 쿼리의 수와 실행 시간을 검증할 수 있는 테스트 라이브러리
- N+1 문제를 테스트 단계에서 미리 발견
- 쿼리 성능을 검증하는 테스트 코드 작성을 자연스럽게 유도
- 슬로우 쿼리 방지로 성능 최적화
- 특정 테이블만 선별적 검증 가능
build.gradle에 의존성을 추가해주세요.
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
dependencies {
// 최신 버전은 상단의 JitPack 배지 참고
testImplementation("com.github.jjh75607:spring-test-util:v0.0.5")
}@SpringBootTest와 함께 테스트 코드에 @ExtendWith(QueryCountTestExtension.class) 어노테이션을 추가하여 사용합니다.
@SpringBootTest
@ExtendWith(QueryCountTestExtension.class)
class MemberServiceTest {
@Autowired
private MemberService memberService;
@Test
void saveMember() {
// given
Member member = new Member("jjh75607", 12);
// when
memberService.createMember(member);
// then
QueryCounterAssertion.assertCounts()
.forTables("member") // member 테이블에 대한 쿼리만 검증
.insert(1) // INSERT 1회 실행 예상
.verify(); // 검증 실행
}
}| 메서드 | 설명 |
|---|---|
forTable(String table) |
특정 테이블에 대한 쿼리 검증 조건을 개별 설정합니다. 체이닝으로 여러 테이블에 각각 다른 조건 설정 가능 |
forTables(String... tables) |
검증할 테이블 이름을 지정합니다. 지정하지 않으면 모든 테이블 쿼리를 검증합니다. |
forTables(List<String> tables) |
리스트 형태로 검증할 테이블 이름 지정 |
select(int expected) |
SELECT 쿼리 실행 횟수 검증 |
insert(int expected) |
INSERT 쿼리 실행 횟수 검증 |
update(int expected) |
UPDATE 쿼리 실행 횟수 검증 |
delete(int expected) |
DELETE 쿼리 실행 횟수 검증 |
others(int expected) |
나머지 쿼리 실행 횟수 검증 |
maxExecutionTimeMs(long ms) |
개별 쿼리 최대 실행 시간을 지정, 초과 시 AssertionError 발생 |
verify() |
설정한 조건으로 쿼리 검증 반드시 호출 |
@Test
void getMember() {
// given
Member member = new Member("test");
memberRepository.save(member);
// when
memberService.getMember(member.getId());
// then
QueryCounterAssertion.assertCounts()
.select(1) // SELECT 1회
.insert(1) // INSERT 1회 (테스트 데이터 삽입)
.verify();
}
@Test
void updateOrderWithExecutionTimeCheck() {
// given
Product product = new Product("item", 1000);
productRepository.save(product);
Order order = new Order(1L, product, 100);
orderRepository.save(order);
// when
orderService.updateOrder(order.getId(), "item2", 200);
// then
QueryCounterAssertion.assertCounts()
.forTables("orders", "products") // 특정 테이블만 검증
.insert(2) // orders, products 각각 INSERT 1회
.select(1) // SELECT 1회
.update(1) // UPDATE 1회
.maxExecutionTimeMs(100) // 개별 쿼리 최대 100ms
.verify();
}
@Test
void verifyQueryCountPerTable() {
// given
Member member = new Member("test");
memberRepository.save(member);
Product product = new Product("item", 1000);
productRepository.save(product);
// when
memberService.getMember(member.getId());
productService.getProducts();
// then - 테이블별로 각각 다른 쿼리 수 검증
QueryCounterAssertion.assertCounts()
.forTable("member").insert(1).select(1)
.forTable("product").insert(1).select(1)
.verify();
}
@Test
void verifyQueryCountPerTableWithExecutionTime() {
// given
Member member = new Member("test");
memberRepository.save(member);
// when
memberService.getMember(member.getId());
// then - 테이블별 쿼리 수 + 실행 시간 검증
QueryCounterAssertion.assertCounts()
.forTable("member").insert(1).select(1).maxExecutionTimeMs(100)
.forTable("orders").select(2).maxExecutionTimeMs(200)
.verify();
}- 예상과 실제 쿼리 횟수가 다르면 AssertionError 발생
- 예상 시간을 초과하는 쿼리가 있으면 AssertionError 발생
java.lang.AssertionError: [Test: {패키지}.{클래스}#{메서드}] Query count assertion failed:
QueryType.SELECT: expected 3, but was 2
java.lang.AssertionError: [Test: {패키지}.{클래스}#{메서드}] Table-specific query count assertion failed:
Table 'member' - QueryType.SELECT: expected 2, but was 1
java.lang.AssertionError: [Test: {패키지}.{클래스}#{메서드}]
Query execution time assertion failed: max=100ms, violations=1
First violation: 120ms > 100ms, type=SELECT
SQL: SELECT * FROM member
java.lang.AssertionError: [Test: {패키지}.{클래스}#{메서드}]
Table 'member' execution time assertion failed: max=100ms, violations=1
First violation: 120ms > 100ms, type=SELECT
SQL: SELECT * FROM member
@Test
void failedVerify() {
// given
Member member = new Member("test");
// when
memberService.createMember(member); // INSERT 2회 발생
// then
QueryCounterAssertion.assertCounts()
.insert(1) // 1회 예상
.verify(); // AssertionError 발생
}
@Test
void failedExecutionTime() {
// given
jdbcTemplate.execute("CALL SLEEP(120)"); // 120ms 쿼리
// then
QueryCounterAssertion.assertCounts()
.maxExecutionTimeMs(100) // 최대 100ms 지정
.verify(); // AssertionError 발생
}