JAVA

[Java] TDD์™€ ์˜ˆ์ œ์™€ ์—ฐ์Šต , querydsl ์Šฌ๋ผ์ด์‹ฑ ํ…Œ์ŠคํŠธ ์„ค์ •

girin_dev 2023. 2. 25. 00:29
728x90
๋ฐ˜์‘ํ˜•

๐Ÿฆ TDD๋ฅผ ์ฒ˜์Œ ์ ‘ํ•œ๊ฒƒ์€ ์ฑ„์šฉ ๊ณต๊ณ ์—์„œ์˜€๋‹ค. ์šฐ๋Œ€์‚ฌํ•ญ์—๋Š” TDD๋ฅผ ์“ด๋‹ค๋Š” ์–˜๊ธฐ๊ฐ€ ์ƒ๋‹นํžˆ ์ž์ฃผ ๋ณด์˜€๋‹ค. 

์ตœ๊ทผ  TDD๋Š” ์ฃฝ์—ˆ๋‹ค๋ผ๋Š” ์ž๋™์™„์„ฑ์ด ๋ณด์ด๋Š” ๊ฒƒ์„ ๋ณด๋ฉด TDD์— ๋Œ€ํ•œ ํšŒ์˜๊ฐ์ด ๋“œ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋งŽ์•„ ๋ณด์ธ๋‹ค. 

์™œ ์œ ํ–‰ํ–ˆ๋Š” ์ง€, ๋น„ํŒ๋ฐ›๋Š” ์ง€, ๊ถ๊ธˆํ•˜๋‹ˆ๊นŒ ์ง์ ‘ ํ•ด๋ณธ๋‹ค.

 

TDD ?

TDD(Test Driven Development)  ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋กœ, ํ…Œ์ŠคํŠธ๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•˜๊ณ  ์ด๋ฅผ ํ†ต๊ณผํ•˜๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ.

 

์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ ํ”„๋กœ์„ธ์Šค๋ฅผ ํšจ์œจ์ ์ด๊ณ  ์•ˆ์ •์ ์œผ๋กœ ๋งŒ๋“ค๋ฉฐ, ํ’ˆ์งˆ์„ ๋†’์ด๋Š” ๋ฐ์— ๊ธฐ์—ฌํ•œ๋‹ค.

 


 

์ ˆ์ฐจ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. 

 

1.ํ…Œ์ŠคํŠธ ์ž‘์„ฑ

  • ๋จผ์ € ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ž‘์„ฑํ•œ๋‹ค. ์ด๋•Œ ํ…Œ์ŠคํŠธ๋Š” ์‹คํŒจํ•˜๋Š” ๊ฒƒ์„ ๊ธฐ๋Œ€ํ•œ๋‹ค.
  • ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋Š” ์ฝ”๋“œ ์ž‘์„ฑ ์ „์— ๋ฏธ๋ฆฌ ์ž‘์„ฑ๋˜๋ฉฐ, ๋ช…ํ™•ํ•˜๊ณ  ๊ฐ„๊ฒฐํ•œ ์š”๊ตฌ์‚ฌํ•ญ์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค.

2.์ฝ”๋“œ ์ž‘์„ฑ

  • ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•˜๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.
  • ์ด๋•Œ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋Š” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๊ฐ€ ํ†ต๊ณผ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ์ตœ์†Œํ•œ์˜ ์ฝ”๋“œ์—ฌ์•ผ ํ•œ๋‹ค

3.ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๋ฐ ํ†ต๊ณผ ์—ฌ๋ถ€ ํ™•์ธ

  • ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ํ†ต๊ณผํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.
  • ์ด๋•Œ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•œ๋‹ค๋ฉด ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ ๋‹ค์‹œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ณ , ํ…Œ์ŠคํŠธ๊ฐ€ ํ†ต๊ณผํ•  ๋•Œ๊นŒ์ง€ ์ด๋ฅผ ๋ฐ˜๋ณตํ•œ๋‹ค

4.์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง

  • ํ…Œ์ŠคํŠธ๊ฐ€ ํ†ต๊ณผ๋œ ์ฝ”๋“œ๋ฅผ ๋ฆฌํŒฉํ† ๋งํ•˜์—ฌ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ, ์œ ์ง€๋ณด์ˆ˜์„ฑ, ํ™•์žฅ์„ฑ ๋“ฑ์„ ๊ฐœ์„ ํ•œ๋‹ค.

์ด๋Ÿฌํ•œ ๋ฐฉ์‹์œผ๋กœ TDD๋Š” ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ ํ”„๋กœ์„ธ์Šค๋ฅผ ๋”์šฑ ์•ˆ์ •์ ์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ๋ฉฐ, ์ฝ”๋“œ์˜ ํ’ˆ์งˆ์„ ๋†’์ผ ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ, TDD๋Š” ์ฝ”๋“œ ๋ณ€๊ฒฝ์‚ฌํ•ญ์— ๋Œ€ํ•œ ์‹ ์†ํ•œ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•˜์—ฌ ๋ฒ„๊ทธ๋ฅผ ๋ฏธ๋ฆฌ ์˜ˆ๋ฐฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค€๋‹ค.

 

 


 

 

๐Ÿฅ• ํ‰์†Œ์— ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ๋ฌธ์„œ๋ฅผ ๋งŒ๋“ ์ ์ด ์žˆ์ง€๋งŒ,

            ์œ ๋‹›ํ…Œ์ŠคํŠธ ์‚ฌ์šฉํ•œ ๊ฒฝํ—˜์€ ๊ทนํžˆ ์ ์—ˆ๋‹ค.

             TDD์˜ 1๋ฒˆ ๋ถ€ํ„ฐ ์ฐจ๋ก€๋กœ ํ•ด๋ณด๋ ค๊ณ  ํ—€๋‹ค.

 

์‹คํŒจํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ž‘์„ฑํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‹คํŒจํ•˜๋Š” ๊ฒƒ์„ ๊ธฐ๋Œ€ํ•œ๋‹ค๋Š” ๊ฒŒ ๋ฌด์Šจ ๋ง์ธ์ง€ ์ดํ•ด๋ฅผ ๋ชปํ–ˆ์ง€๋งŒ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋ผ๋Š” ์ด์•ผ๊ธฐ ๊ฐ™๋‹ค.

 

1. ์‹คํŒจํ•˜๋Š” ์ผ€์ด์Šค

์ปดํŒŒ์ผ๋„ ์•ˆ๋˜๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰์กฐ์ฐจ ํ•  ์ˆ˜ ์—†๋‹ค.

 

TDD๋ฅผ ํ•ด๋ณธ ๊ฒฝํ—˜์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ์™œ ์ด๋Ÿฐ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์ง€ ๋ชจ๋ฅด๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ ๋‹ค. ํด๋ž˜์Šค๋ถ€ํ„ฐ ์ˆœ์„œ๋Œ€๋กœ ์ปดํŒŒ์ผ์ด ๋˜๋Š” ์‹์œผ๋กœ ์ž‘์„ฑํ•˜๊ณ  ์‹ถ์€ ๋งˆ์Œ์ด ๋“ค์—ˆ๋‹ค. ์ปดํŒŒ์ผ๋„ ์•ˆ๋˜๊ณ  ๋นจ๊ฐ„์ค„๋งŒ ๋œจ๊ฒŒ๋”ํ•˜๋Š” ํƒ€์ž๋ฅผ ์น˜๋Š”๊ฒŒ ์ŠคํŠธ๋ ˆ์Šค๋ฅผ ์ค€๋‹ค.

 

๊ทธ๋ž˜๋„, ์ด๋Ÿฐ ์‹์œผ๋กœ, ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค๊ณ  ๋ฉ”์†Œ๋“œ๋ฅผ ๋งŒ๋“ค์ž๋ผ๊ณ  ์˜ˆ์ƒํ•˜๋ฉฐ ์ตœ๋Œ€ํ•œ ์ž‘์„ฑ์„ ํ•ด๋ณธ๋‹ค.

 

 

2. ํ†ต๊ณผํ•˜๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  

       ๋ชจ๋กœ ๊ฐ€๋„ ์„œ์šธ๋กœ ๊ฐ€๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.

// 3๋งŒ ๋ฆฌํ„ดํ•˜๋ฉด ํ…Œ์ŠคํŠธ๋Š” ํ†ต๊ณผํ•œ๋‹ค.
class Calculator {
    public int calculate(int a, int b) {
        return 3;
    }
}

 

 

 

3. ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ํ†ต๊ณผํ•˜๋Š” ์ง€ ํ™•์ธํ•œ๋‹ค.

3๋ฆฌํ„ดํ•˜๋ฉด ํ…Œ์ŠคํŠธ ํ†ต๊ณผํ•˜๋‹ˆ๊นŒ, 3์„ ๋ฆฌํ„ดํ•œ๋‹ค.

 

 

3. ์ฝ”๋“œ๋ฅผ ๋ฆฌํŒฉํ† ๋งํ•œ๋‹ค. 

         

๋‹จ์ˆœํžˆ ์ฝ”๋“œ๋งŒ ์ˆ˜์ •ํ•˜๋Š” ๊ฒŒ ์•„๋‹Œ ๊ฐ€๋…์„ฑ, ์œ ์ง€๋ณด์ˆ˜์„ฑ, ํ™•์žฅ์„ฑ ๋“ฑ์„ ๊ฐœ์„ ํ•˜๊ฒŒ๋” ํ•ด์•ผ ํ•œ๋‹ค.

 

 

 

 

 

 

 


 

์—ฐ์Šตํ•œ ๊ฒƒ์„ ๋ฐ”ํƒ•์œผ๋กœ ์‹ค์ œ ๊ฒŒ์‹œํŒ์— ๊ธ€์„ ์“ฐ๊ธฐ ์œ„ํ•ด ์—ฐ์Šตํ•˜๋ฉฐ TDD๋ฅผ ์ตํžˆ๋ ค ํ•ด๋ณด์•˜๋‹ค. 

 

ํŽ˜์ด์ง•์„ ์œ„ํ•œ ์กฐํšŒ๋ฅผ TDD๋กœ ์—ฐ์Šตํ•œ๋‹ค๋ฉด,

 

1. ์‹คํŒจํ•˜๋Š” ํ…Œ์ŠคํŠธ 

 

2. ํ†ต๊ณผ๋ฅผ ์œ„ํ•œ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค๊ณ ,

// PostRepositoryCustom.java

public List<PostDto> getPostsWithSearchPaging(Pageable pageable, String search) {
            List<PostDto> posts = new ArrayList<>();
        posts.add(new PostDto(10L
                ,"title"
                ,"content"
                , new UserDto("name","email","1123")
                ,null));
        return posts;
 }

 

 

3. ํ…Œ์ŠคํŠธ ํ™•์ธํ•˜๊ณ , 

 

4. ๋ฆฌํŒฉํ† ๋งํ•œ๋‹ค. 

public List<PostDto> getPostsWithSearchPaging(Pageable pageable, String search) {
        BooleanBuilder booleanBuilder = new BooleanBuilder();
        if (StringUtils.hasText(search)) {
            booleanBuilder.or(qPost.postTitle.contains(search));
            booleanBuilder.or(qPost.postContent.contains(search));
            booleanBuilder.or(qPost.user.nickname.contains(search));
        }
        booleanBuilder.and(qPost.isDeleted.eq('N'));

        return queryFactory.select(Projections.fields(PostDto.class
                ,qPost.id
                ,qPost.postTitle
                ,qPost.postContent
                ,qPost.createdBy
                ,qPost.createDt
                ,qPost.recommendCount
        )).from(qPost)
            .where(booleanBuilder)
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .orderBy(qPost.createDt.desc())
            .fetch();
    }
    
   
    // TDD
    // ์™„์„ฑํ˜• ์ฝ”๋“œ๊ฐ€ ๋  ๋•Œ๊นŒ์ง€ ํ…Œ์ŠคํŠธํ•˜๋ฉฐ ๋ฆฌํŒฉํ† ๋ง.

 

๊ฐœ์ธ์ ์œผ๋กœ ๋А๋‚€ ์ƒ๊ฐ์€ ํ‰์†Œ JUnit์„ ์ž˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๊ธฐ์— Swagger-ui๋ฅผ ํ†ตํ•œ ํ…Œ์ŠคํŠธ๋‚˜ postMan์„ ํ†ตํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์ž์ฃผ ํ•˜์˜€๋‹ค.   

 

Swagger-ui๋ฅผ ํ†ตํ•œ ํ…Œ์ŠคํŠธ ํ˜น์€ postMan์„ ํ†ตํ•œ ํ…Œ์ŠคํŠธ๋ณด๋‹ค, ์—ฌ๋Ÿฌ๋ฒˆ์˜ ํ…Œ์ŠคํŠธ๋ฅผ ํ•œ๊บผ๋ฒˆ์— ํ•˜๊ฑฐ๋‚˜, ๊ฐ๊ธฐ ๋‹ค๋ฅธ ํ…Œ์ŠคํŠธ๋“ค์„ ๋”ฐ๋กœ๋”ฐ๋กœ ํ•  ๊ฒฝ์šฐ ์‹œ๊ฐ„์ด ๋งŽ์ด ๋‹จ์ถ•๋จ์„ ๋А๊ผˆ๋‹ค. 

--> ๋ฌผ๋ก  ์ด๋Š” TDD์˜ ์žฅ์ ์ด๊ธฐ๋„ ํ•˜์ง€๋งŒ,  Junit์„ ํ™œ์šฉํ•œ ํ…Œ์ŠคํŠธ์˜ ์žฅ์ ์ด ๋” ํฌ๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

 

 

 

 

TDD ๋ฐฉ์‹์˜ ๋‹จ์  ? 

๊ฐœ๋ฐœ ์†๋„๊ฐ€ ๋А๋ ค์ง„๋‹ค. 

SI ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์†Œํ”„ํŠธ์›จ์–ด์˜ ํ’ˆ์งˆ๋ณด๋‹ค ๋‚ฉ๊ธฐ์ผ ์ค€์ˆ˜๊ฐ€ ํ›จ์”ฌ ์ค‘์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— TDD ๋ฐฉ์‹์„ ์ž˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

Test๋ฅผ ์œ„ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์‹œ๊ฐ„๋„ ๊ฐœ๋ฐœ ์‹œ๊ฐ„์— ํฌํ•จ๋œ๋‹ค. 

 

 

 

 

TDD is dead. Long live testing ?

       ํ•œ์ฐธ ์ง€๋‚œ TDD ๋…ผ์Ÿ์ธ๋ฐ ์ด์ œ์•ผ ์•ˆ๋‹ค๋Š” ๊ฒƒ์ด ๋ถ€๋„๋Ÿฝ๋‹ค.

 

๐Ÿ”Ž tdd๋Š” ์ฃฝ์—ˆ๋‹ค: Google ๊ฒ€์ƒ‰

 

www.google.com

 

 

 


 

 

querydsl ์Šฌ๋ผ์ด์‹ฑ ํ…Œ์ŠคํŠธ ์„ค์ • ?

 

 

Junit ๋ฒ„์ „ : 5. xx ์ž…๋‹ˆ๋‹ค.

 

TestClass ์„ค์ • :

 

 

TestConfig ์„ค์ • : 

 

 

 

์œ„์™€ ๊ฐ™์€ ์„ค์ •์€, 

 

Junit ์—์„œ  ์ƒ์„ฑ์ž ์ฃผ์ž…๋ฐฉ์‹์„ ์ธ์‹ํ•˜์ง€ ๋ชปํ•ด์„œ์ด๋‹ค.

๋”ฐ๋ผ์„œ Spring์—๊ฒŒ ์œ„์ž„ํ•˜์—ฌ Autowired๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด ์˜์กด์„ฑ์„ ์ฃผ์ž… ๋ฐ›์•„์˜จ๋‹ค. 

 

 

 

๋”ฐ๋ผ์„œ, TestConfig๋ฅผ ํ†ตํ•ด ์„ค์ •์„ ์ฃผ์ž…ํ•˜์ง€ ์•Š๊ณ  ์“ฐ๋ฉฐ,  ์œ„์™€ ๊ฐ™์€ ์ปค์Šคํ…€ Repository์—์„œ JPAQueryFactory๋ฅผ ์ƒ์„ฑ์ž ๋ฐฉ์‹์œผ๋กœ ์“ธ ๊ฒฝ์šฐ, 

 

Junit Test ์‹œ์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—๋Ÿฌ๋ฅผ ๋งŒ๋‚˜๊ฒŒ ๋œ๋‹ค. 

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.org.lessortalk.post.service.PostServiceImplTest': Unsatisfied dependency expressed through field 'postRepositoryCustom'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.org.lessortalk.post.repository.PostRepositoryCustom' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

 

 

Bean ๊ฐ์ฒด๋ฅผ ์ฐพ์ง€ ๋ชปํ•˜๋ฏ€๋กœ ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค์˜ ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•œ๋‹ค๋ฉด, ์œ„ ์„ค์ •์„ ๋จผ์ € ํ™•์ธํ•ด๋ณด์•„์•ผ ํ•œ๋‹ค. 

 

 

 

 

 

 

 

์ฐธ๊ณ  : https://jyami.tistory.com/124

 

QueryDSL ์Šฌ๋ผ์ด์‹ฑ ํ…Œ์ŠคํŠธ(@DataJpaTest) / Test Bean ๋“ฑ๋ก

QueryDSL ์Šฌ๋ผ์ด์‹ฑ ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๊ธฐ๋ก์„ ๋‚จ๊ธด๋‹ค. ๋‚ด๊ฐ€ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๋Š” Querydsl ํŒจํ„ด์€ CustomRepository๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ์‹์ด๋‹ค. docs.spring.io/spring-data/jpa/docs/2.1.3.RELEASE/reference/html/#repositories.custom-implementa

jyami.tistory.com

 

 

 

 

320x100
๋ฐ˜์‘ํ˜•