Skip to content

Annotation-based library for verifying JPA query count, performance, lazy loading, and transaction behavior in Spring Boot tests.

License

Notifications You must be signed in to change notification settings

Hyeon-moGu/QueryKeeper

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

20 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐ŸŒฑ QueryKeeper

JPA ์ฟผ๋ฆฌ ์‹คํ–‰ ๊ฒ€์ฆ ๋ฐ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ์–ด๋…ธํ…Œ์ด์…˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

QueryKeeper๋Š” Spring Boot + JPA ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ์‹คํ–‰๋˜๋Š” SQL ์ฟผ๋ฆฌ ์ˆ˜, ์‹คํ–‰ ์‹œ๊ฐ„, DB ์ ‘๊ทผ ์—ฌ๋ถ€ ๋“ฑ์„ ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฒ€์ฆํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ „์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ์™ธ๋ถ€ APM์ด๋‚˜ JDBC ํ”„๋ก์‹œ ์—†์ด, ์ˆœ์ˆ˜ Java ์ฝ”๋“œ๋กœ ๊ตฌํ˜„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํ•ต์‹ฌ JDBC ๊ตฌ์„ฑ ์š”์†Œ(PreparedStatement, Connection, DataSource)๋ฅผ ์ง์ ‘ ๊ฐ์‹ธ ๋‚ฎ์€ ์ˆ˜์ค€์—์„œ ์ฟผ๋ฆฌ๋ฅผ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค.

โœ”๏ธ Java 8 ~ 17+, Spring Boot 2.7 ~ 3.2+, Hibernate 5.6 ~ 6.3+, JUnit 5.8+ ํ™˜๊ฒฝ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

โœ… ๋ณ„๋„ ์„ค์ • ์—†์ด ๋ฐ”๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ. ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค์— @EnableQueryKeeper ๋งŒ ์ถ”๊ฐ€
โœ… ์ฟผ๋ฆฌ ์„ฑ๋Šฅ ํšŒ๊ท€๋ฅผ ํ…Œ์ŠคํŠธ ๋‹จ๊ณ„์—์„œ ๊ฐ์ง€
โœ… @ExpectQuery, @ExpectDetachedAccess, @ExpectTime, @ExpectDuplicateQuery ๊ฐ™์€ ์ง๊ด€์ ์ธ ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ๊ตฌํ˜„
โœ… N+1 ๋ฌธ์ œ, ๋ถˆํ•„์š”ํ•œ DB ํ˜ธ์ถœ, ๋А๋ฆฐ ์ฟผ๋ฆฌ๋ฅผ ํ…Œ์ŠคํŠธ ์ค‘ ํƒ์ง€
โœ… PreparedStatement, Connection ๋ฐ DataSource๋ฅผ ์ง์ ‘ ๋ž˜ํ•‘


1๏ธโƒฃ ๊ธฐ๋Šฅ ์†Œ๊ฐœ

์–ด๋…ธํ…Œ์ด์…˜ ์„ค๋ช…
@EnableQueryKeeper ๋ชจ๋“  QueryKeeper ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”
@ExpectQuery ํ…Œ์ŠคํŠธ ์ค‘ ์‹ค์‹œ ์ˆ˜ํ–‰๋œ ์ถ”์ ์˜ ๊ฐœ์ˆ˜๋ฅผ ๋กœ๊น… ๋ฐ ๊ฒ€์‚ฌ
@ExpectDuplicateQuery ๋™์ผํ•œ SQL ์ฟผ๋ฆฌ(ํŒŒ๋ผ๋ฏธํ„ฐ ํฌํ•จ)๊ฐ€ ๋ฐ˜๋ณต ์‹คํ–‰๋  ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํŒจ ์ฒ˜๋ฆฌ
@ExpectDetachedAccess ํŠธ๋žœ์žญ์…˜์ด ์ข…๋ฃŒ๋œ ํ›„ LAZY ํ•„๋“œ์— ์ ‘๊ทผํ•˜์—ฌ ๋ฐœ์ƒํ•˜๋Š” LazyInitializationException๋ฅผ ๊ฐ์ง€
@ExpectTime ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ๊ฐ„ ์ œํ•œ (ms)
@ExpectNoDb ํ…Œ์ŠคํŠธ ์ค‘ DB ์ ‘๊ทผ์ด ์—†์–ด์•ผ ํ†ต๊ณผ
@ExpectNoTx ํ…Œ์ŠคํŠธ ์ค‘ ํŠธ๋žœ์žญ์…˜์ด ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์œผ๋ฉด ์‹คํŒจ (strict = true์ผ ๊ฒฝ์šฐ, ์ฝ๊ธฐ ์ „์šฉ๋„ ์‹คํŒจ)
๐Ÿ“˜ ์–ด๋…ธํ…Œ์ด์…˜๋ณ„ ์ƒ์„ธ ์„ค๋ช… (ํด๋ฆญํ•˜์—ฌ ํŽผ์น˜๊ธฐ)

@ExpectQuery

ํ…Œ์ŠคํŠธ ์ค‘ ์‹คํ–‰๋œ SQL ์ฟผ๋ฆฌ ์ˆ˜๋ฅผ ๊ธฐ๋กํ•˜๊ณ  ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.

  • ํŒŒ๋ผ๋ฏธํ„ฐ:

    • select (๊ธฐ๋ณธ๊ฐ’: -1) โ€” ์˜ˆ์ƒ SELECT ์ฟผ๋ฆฌ ์ˆ˜
    • insert (๊ธฐ๋ณธ๊ฐ’: -1) โ€” ์˜ˆ์ƒ INSERT ์ฟผ๋ฆฌ ์ˆ˜
    • update (๊ธฐ๋ณธ๊ฐ’: -1) โ€” ์˜ˆ์ƒ UPDATE ์ฟผ๋ฆฌ ์ˆ˜
    • delete (๊ธฐ๋ณธ๊ฐ’: -1) โ€” ์˜ˆ์ƒ DELETE ์ฟผ๋ฆฌ ์ˆ˜
  • ๋™์ž‘ ๋ฐฉ์‹: ๊ธฐ๋ณธ์ ์œผ๋กœ ์ด ์–ด๋…ธํ…Œ์ด์…˜์€ ์‹คํ–‰๋œ ๋ชจ๋“  SQL ์ฟผ๋ฆฌ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํฌํ•จํ•œ ์™„์ „ํ•œ ํ˜•ํƒœ๋กœ ์ถœ๋ ฅํ•˜๋ฉฐ, ์‹คํ–‰ ์‹œ๊ฐ„๊ณผ ํ˜ธ์ถœ ์œ„์น˜๋„ ํ•จ๊ป˜ ๋กœ๊น…ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ select, insert ๋“ฑ์˜ ๊ธฐ๋Œ€ ํšŸ์ˆ˜(0 ์ด์ƒ)๊ฐ€ ์ง€์ •๋œ ๊ฒฝ์šฐ, ์‹ค์ œ ์‹คํ–‰๋œ ์ฟผ๋ฆฌ ์ˆ˜์™€ ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด ํ…Œ์ŠคํŠธ๋Š” ์‹คํŒจ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋Œ€๊ฐ’์ด ์„ค์ •๋˜์ง€ ์•Š๋”๋ผ๋„ ๋ชจ๋“  ํ…Œ์ŠคํŠธ์—์„œ ์ฟผ๋ฆฌ ๋ชฉ๋ก์€ ํ•ญ์ƒ ๋™์ผํ•œ ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. ์ถœ๋ ฅ ๋‚ด์šฉ์€ ์ฟผ๋ฆฌ ์œ ํ˜•, ์‹คํ–‰ ์‹œ๊ฐ„(ms), ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ํฌํ•จ๋œ ์‹ค์ œ SQL๋ฌธ, ํ˜ธ์ถœ ์œ„์น˜(ํด๋ž˜์Šค๋ช…#๋ฉ”์„œ๋“œ:๋ผ์ธ ๋ฒˆํ˜ธ) ๋“ฑ์„ ํฌํ•จํ•˜๋ฉฐ ๋””๋ฒ„๊น…์ด๋‚˜ ์„ฑ๋Šฅ ๋ถ„์„์— ๋งค์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

@ExpectDuplicateQuery

๋™์ผํ•œ SQL ์ฟผ๋ฆฌ(ํŒŒ๋ผ๋ฏธํ„ฐ ํฌํ•จ)๊ฐ€ ์—ฌ๋Ÿฌ ๋ฒˆ ์‹คํ–‰๋  ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํŒจ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

  • ํŒŒ๋ผ๋ฏธํ„ฐ:

    • max (์„ ํƒ, ๊ธฐ๋ณธ๊ฐ’: 0) โ€” ํ—ˆ์šฉ๋˜๋Š” ์ค‘๋ณต ์ฟผ๋ฆฌ์˜ ์ตœ๋Œ€ ๊ฐœ์ˆ˜
  • ์ž‘๋™ ๋ฐฉ์‹:
    ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ค‘ ๋ฐœ์ƒํ•œ ๋ชจ๋“  SQL ์ฟผ๋ฆฌ์™€ ๊ทธ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ถ”์ ํ•˜์—ฌ,
    ๋™์ผํ•œ ์ฟผ๋ฆฌ(๋ฌธ์ž์—ด ๋ฐ ํŒŒ๋ผ๋ฏธํ„ฐ ์กฐํ•ฉ)๊ฐ€ ๋ฐ˜๋ณต ์‹คํ–‰๋  ๊ฒฝ์šฐ ์ค‘๋ณต์œผ๋กœ ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค.
    ์ด ์ค‘๋ณต ์ฟผ๋ฆฌ ์ˆ˜๊ฐ€ max ๊ฐ’์„ ์ดˆ๊ณผํ•˜๋ฉด ํ…Œ์ŠคํŠธ๋Š” ์‹คํŒจํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋ฃจํ”„ ๋‚ด ๋™์ผ SELECT ๋ฐ˜๋ณต, ์‹ค์ˆ˜๋กœ ๋ฐœ์ƒํ•œ N+1 ๋ฌธ์ œ ๋“ฑ์„ ์กฐ๊ธฐ์— ๊ฐ์ง€ํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

@ExpectDetachedAccess

ํŠธ๋žœ์žญ์…˜์ด ์ข…๋ฃŒ๋œ ์ƒํƒœ์—์„œ ์ง€์—ฐ ๋กœ๋”ฉ ํ•„๋“œ์— ์ž˜๋ชป ์ ‘๊ทผํ•  ๊ฒฝ์šฐ ๋ฐœ์ƒํ•˜๋Š” LazyInitializationException ์„ ๊ฐ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, JPA ์—”ํ‹ฐํ‹ฐ๊ฐ€ detached ์ƒํƒœ์ผ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ž˜๋ชป๋œ Lazy ํ•„๋“œ ์ ‘๊ทผ์„ ํ…Œ์ŠคํŠธ ์ค‘ ์กฐ๊ธฐ์— ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ํŒŒ๋ผ๋ฏธํ„ฐ: ์—†์Œ

  • ๋™์ž‘ ๋ฐฉ์‹: ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ค‘ ๋ฐœ์ƒํ•œ LazyInitializationException์„ AOP๋กœ ๊ฐ€๋กœ์ฑ„์–ด, ์–ด๋–ค ์—”ํ‹ฐํ‹ฐ์˜ ์–ด๋–ค ํ•„๋“œ๊ฐ€ ์ž˜๋ชป ์ ‘๊ทผ๋˜์—ˆ๋Š”์ง€ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํ…Œ์ŠคํŠธ์—์„œ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ Lazy ์ ‘๊ทผ์„ ๋น ๋ฅด๊ฒŒ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โš ๏ธ ํŠธ๋žœ์žญ์…˜ ์™ธ๋ถ€์—์„œ์˜ ๋น„์ •์ƒ์ ์ธ Lazy ์ ‘๊ทผ(LazyInitializationException) ๋งŒ ํƒ์ง€ํ•ฉ๋‹ˆ๋‹ค.

@ExpectTime

ํ…Œ์ŠคํŠธ๊ฐ€ ์ง€์ •๋œ ์‹œ๊ฐ„ ๋‚ด์— ์™„๋ฃŒ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ํŒŒ๋ผ๋ฏธํ„ฐ:

    • value (ํ•„์ˆ˜) โ€” ํ—ˆ์šฉ๋˜๋Š” ์ตœ๋Œ€ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ๊ฐ„ (ms ๋‹จ์œ„)
  • ๋™์ž‘ ๋ฐฉ์‹: ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ „์ฒด ์‹œ๊ฐ„(์„ค์ •, DB ์ฟผ๋ฆฌ ๋“ฑ ํฌํ•จ)์„ ์ธก์ •ํ•˜๋ฉฐ, ์„ค์ •ํ•œ ์‹œ๊ฐ„ ์ด์ƒ ์†Œ์š”๋˜๋ฉด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

@ExpectNoDb

ํ…Œ์ŠคํŠธ ์ค‘ ์–ด๋–ค ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ ‘๊ทผ๋„ ์—†์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ํŒŒ๋ผ๋ฏธํ„ฐ: ์—†์Œ

  • ๋™์ž‘ ๋ฐฉ์‹: SELECT, INSERT, UPDATE, DELETE ๋“ฑ ๋ชจ๋“  ์ฟผ๋ฆฌ ์‹คํ–‰์„ ๊ฐ์ง€ํ•˜๋ฉฐ, ๋‹จ ํ•˜๋‚˜๋ผ๋„ ๋ฐœ์ƒํ•˜๋ฉด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ์ˆœ์ˆ˜ ๋กœ์ง ๋˜๋Š” ์บ์‹œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

@ExpectNoTx

ํ…Œ์ŠคํŠธ๊ฐ€ ํŠธ๋žœ์žญ์…˜ ์™ธ๋ถ€์—์„œ ์‹คํ–‰๋˜์–ด์•ผ ํ•จ์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.

  • ํŒŒ๋ผ๋ฏธํ„ฐ:

    • strict (๊ธฐ๋ณธ๊ฐ’: true) โ€” readOnly ํŠธ๋žœ์žญ์…˜๊นŒ์ง€ ๊ธˆ์ง€ํ• ์ง€ ์—ฌ๋ถ€
  • ๋™์ž‘ ๋ฐฉ์‹: ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ค‘ ํ™œ์„ฑ ํŠธ๋žœ์žญ์…˜์ด ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. strict=true์ธ ๊ฒฝ์šฐ, @Transactional(readOnly = true)๋„ ์‹คํŒจ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

์ฝ”๋“œ ์˜ˆ์‹œ

์•„๋ž˜ ์˜ˆ์‹œ๋Š” ์ผ๋ถ€ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•˜๋„๋ก ์„ค๊ณ„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

@SpringBootTest
@EnableQueryKeeper       // โœ… ๊ธฐ๋Šฅํ™œ์„ฑํ™”ํ™”
class UserRepositoryTest {

        ....

         @Test
         @ExpectQuery(select = 1, insert = 1) // โŒ ์‹คํŒจ
         @ExpectTime(500)                     // โœ… ์„ฑ๊ณต
         @ExpectNoTx(strict = false)          // โœ… ์„ฑ๊ณต
         @ExpectNoDb                          // โŒ ์‹คํŒจ
         @ExpectDuplicateQuery                // โŒ ์‹คํŒจ
         void testCombinedAssertions() {
             User user = new User("Alice", "alice@example.com");
             user.addRole(new Role("ADMIN"));
             user.addRole(new Role("USER"));
             userRepository.save(user);
             userRepository.findAll();
         
             entityManager.clear();
             List<User> users = userRepository.findAll();
             users.get(0).getRoles().size();
         
             int sum = 0;
             for (int i = 0; i < 1000; i++)
                 sum += i;
             assertThat(sum).isGreaterThan(0);
         }
         
         @Test
         @ExpectDetachedAccess      // โŒ ์‹คํŒจ
         void testDetachedAccess() {
             userService.triggerDetachedAccess();
         }
}

์ถœ๋ ฅ ์˜ˆ์‹œ

UserRepositoryTest > testDetachedAccess() STANDARD_OUT
    2025-01-01T12:00:00.000+00:00  INFO 7475 --- [    Test worker] c.q.junit.QueryKeeperExtension           : 
    [QueryKeeper] โ–ถ ExpectDetachedAccess X FAILED - Entity: Role
      โ€ข Field: roles
      โ€ข Access Path: User.roles
      โ€ข Root Entity: User

UserRepositoryTest > testCombinedAssertions() STANDARD_OUT
    2025-06-16 19:39:14.450  INFO 2484 --- [    Test worker] c.q.junit.QueryKeeperExtension           : 
    [QueryKeeper] โ–ถ ExpectNoTx โœ“ PASSED - No transaction in testCombinedAssertions()
    [QueryKeeper] โ–ถ ExpectTime โœ“ PASSED - testCombinedAssertions took 11ms (expected <= 500ms)
    [QueryKeeper] โ–ถ ExpectQuery X FAILED
    --------------------------------------------------------
    Expected - (SELECT: 1, INSERT: 1), Actual - (SELECT: 2, INSERT: 3)
    --------------------------------------------------------
    Total Queries: 8
    --------------------------------------------------------
    1. [OTHER] (0 ms)
    SQL     : call next value for hibernate_sequence
    Caller  : com.example.demo.UserRepositoryTest#testCombinedAssertions:48
    --------------------------------------------------------
    2. [OTHER] (0 ms)
    SQL     : call next value for hibernate_sequence
    Caller  : com.example.demo.UserRepositoryTest#testCombinedAssertions:48
    --------------------------------------------------------
    3. [OTHER] (0 ms)
    SQL     : call next value for hibernate_sequence
    Caller  : com.example.demo.UserRepositoryTest#testCombinedAssertions:48
    --------------------------------------------------------
    4. [INSERT] (0 ms)
    SQL     : insert into users (email, name, id) values ('alice@example.com', 'Alice', 3)
    Caller  : com.example.demo.UserRepositoryTest#testCombinedAssertions:48
    --------------------------------------------------------
    5. [INSERT] (0 ms)
    SQL     : insert into roles (name, user_id, id) values ('ADMIN', 3, 4)
    Caller  : com.example.demo.UserRepositoryTest#testCombinedAssertions:48
    --------------------------------------------------------
    6. [INSERT] (0 ms)
    SQL     : insert into roles (name, user_id, id) values ('USER', 3, 5)
    Caller  : com.example.demo.UserRepositoryTest#testCombinedAssertions:48
    --------------------------------------------------------
    7. [SELECT] (0 ms)
    SQL     : select user0_.id as id1_1_, user0_.email as email2_1_, user0_.name as name3_1_ from users user0_
    Caller  : com.example.demo.UserRepositoryTest#testCombinedAssertions:49
    --------------------------------------------------------
    8. [SELECT] (0 ms)
    SQL     : select user0_.id as id1_1_, user0_.email as email2_1_, user0_.name as name3_1_ from users user0_
    Caller  : com.example.demo.UserRepositoryTest#testCombinedAssertions:52
    --------------------------------------------------------
    [QueryKeeper] โ–ถ ExpectNoDb X FAILED - 8 DB queries were executed in testCombinedAssertions()
    [QueryKeeper] โ–ถ ExpectDuplicateQuery X FAILED - Found 1 duplicate queries (allowed: 0)
      โ€ข Duplicate [2x] โ†’ select user0_.id as id1_1_, user0_.email as email2_1_, user0_.name as name3_1_ from users user0_

2๏ธโƒฃ ์„ค์น˜๋ฐฉ๋ฒ•

๋กœ๊น… ์ฃผ์˜์‚ฌํ•ญ

Querykeeper์€ ๋กœ๊ทธ ์ถœ๋ ฅ์„ ์œ„ํ•ด SLF4J๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
Spring Boot๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๋ณ„๋„ ์„ค์ •์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (spring-boot-starter-logging์— ํฌํ•จ)
Spring Boot๊ฐ€ ์•„๋‹Œ ํ™˜๊ฒฝ์—์„œ๋Š” ๋‹ค์Œ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€:

runtimeOnly 'ch.qos.logback:logback-classic:1.4.14'

A. ๋กœ์ปฌ Maven์— ๋ฐฐํฌ ํ›„ ์‚ฌ์šฉ

make publish
dependencies {
    testImplementation 'com.querykeeper:querykeeper:1.1.0'
}

B. ์ง์ ‘ JAR ํŒŒ์ผ ์‚ฌ์šฉ

testImplementation files('libs/querykeeper-1.1.0.jar')

์˜ต์…˜: ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ์ถœ๋ ฅ(build.gradle ์ถ”๊ฐ€)

test {
    useJUnitPlatform()

    testLogging {
        events "passed", "skipped", "failed"
        showStandardStreams = true
    }
}

3๏ธโƒฃ ๊ถŒ์žฅ ์‚ฌ์šฉ ํ™˜๊ฒฝ

  • Java 8 ~ Java 17+
  • Spring Boot 2.7.x ~ 3.2+
  • Hibernate 5.6.x ~ 6.3+
  • JUnit Jupiter 5.8+

์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” Spring Boot + JPA ํ™˜๊ฒฝ์„ ์ „์ œ๋กœ ์•„๋ž˜ ์˜์กด์„ฑ์ด ํ•จ๊ป˜ ์žˆ์–ด์•ผ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'com.querykeeper:querykeeper:1.1.0'
}

About

Annotation-based library for verifying JPA query count, performance, lazy loading, and transaction behavior in Spring Boot tests.

Topics

Resources

License

Stars

Watchers

Forks