ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์— ๋Œ€ํ•œ ์ •๋ฆฌ ๋ฐ ๊ณ ๋ฏผ ๊ธฐ๋ก

์ด์ „์— ํ–ˆ๋˜ ํ”„๋กœ์ ํŠธ๋“ค์— ๋Œ€ํ•ด์„  ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋Š” ์ž‘์„ฑํ•˜์ง€ ์•Š์•˜๋‹ค. ์ž‘์„ฑํ–ˆ๋‹ค ํ•ด๋„ ์™œ ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š”์ง€๋„ ์ž˜ ๋ชจ๋ฅด๊ณ  ๋Œ€์ถฉ ๋„์ ์—ฌ๋ณธ๊ฒŒ ๋‹ค์˜€๋‹ค. 

 

tickerBell ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ํ†ตํ•ฉํ…Œ์ŠคํŠธ, ๋‹จ์œ„ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉฐ ์™„์„ฑ๋„ ๋†’์€ ํ”„๋กœ์ ํŠธ๋ฅผ ์™„์„ฑํ•ด๋ณด๋ ค ํ•œ๋‹ค.

 

์šฐ์„  ๋‹จ์œ„ํ…Œ์ŠคํŠธ์— ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” Mock, Mockito์— ๋Œ€ํ•ด ์†Œ๊ฐœํ•˜๊ณ ์ž ํ•œ๋‹ค.

 

 

Mock์ด๋ž€?

Mock์€ ํ•œ๊ธ€๋กœ "๋ชจ์˜, ๊ฐ€์งœ์˜"๋ผ๋Š” ๋œป์œผ๋กœ ํ…Œ์ŠคํŠธํ•  ๋•Œ ํ•„์š”ํ•œ ์‹ค์ œ ๊ฐ์ฒด์™€ ๋™์ผํ•œ ๋ชจ์˜ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ํ…Œ์ŠคํŠธ์˜ ํšจ์šฉ์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค. 

 

์˜ˆ์ œ๋ฅผ ๋ณด๋ฉด์„œ ์™œ Mock์„ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์•Œ์•„๋ณด์ž

 

@Service
public class StudyService {
    
    public Student getStudent() {
        // DB์—์„œ study ํ…Œ์ด๋ธ” ์กฐํšŒ (๋ถ€ํ•˜๊ฐ€ ๋งŽ์ด ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…)
    }
}

 

์œ„ ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•œ๋‹ค๋ฉด ๋งค๋ฒˆ ํ…Œ์ŠคํŠธํ•  ๋•Œ๋งˆ๋‹ค DB๋ฅผ ์ฝ์–ด์˜ค๊ฒŒ๋œ๋‹ค. ๋งŒ์•ฝ ์œ„ ๋กœ์ง์ด ๋ถ€ํ•˜๊ฐ€ ๋งŽ์ด ๊ฑธ๋ฆฌ๊ณ  ์ˆ˜ํ–‰ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฐ๋‹ค๋ฉด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š”๋ฐ ์ƒ๋‹นํ•œ ์‹œ๊ฐ„์ด ์†Œ์š”๋œ๋‹ค. 

 

๊ทธ๋ž˜์„œ Student๋ฅผ DB์—์„œ ์ฝ์–ด์˜ค์ง€ ์•Š๊ณ  ์ด๋ฅผ ๊ฐ€์งœ ๊ฐ์ฒด ์ฆ‰, mock์œผ๋กœ ๋งŒ๋“ค์–ด์„œ DB์— ์žˆ๋Š” ํ…Œ์ด๋ธ” ์ ‘๊ทผ์„ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค. 

mock ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๋ฉด ํ…Œ์ŠคํŠธ ์‹œ๊ฐ„์„ ์ค„์ด๋ฉด์„œ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค ์†Œ๋น„๋ฅผ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค

 

๋˜ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ๋‚˜์˜จ ์ž‘์—…์— ๋Œ€ํ•ด์„œ๋„ mock ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๊ตฌํ˜„ ์ „์— ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Mockito๋ž€?

mock์„ ์‰ฝ๊ฒŒ ๋งŒ๋“ค๊ณ  mock์˜ ํ–‰๋™์„ ์ •ํ•˜๋Š” stubbing, ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ verify ๋“ฑ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด์ฃผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ด๋‹ค.

 

 

@Mock

@Mock์œผ๋กœ ๋งŒ๋“  mock ๊ฐ์ฒด๋Š” ๊ฐ€์งœ ๊ฐ์ฒด์ด๋ฉฐ ๊ทธ ์•ˆ์— ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœํ•ด์„œ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋ฐ˜๋“œ์‹œ ์Šคํ„ฐ๋น™(stubbing)์„ ํ•ด์•ผํ•œ๋‹ค.

 

 

@Spy

@Spy๋กœ ๋งŒ๋“  mock ๊ฐ์ฒด๋Š” ์ง„์งœ ๊ฐ์ฒด์ด๋ฉฐ ๋ฉ”์†Œ๋“œ ์‹คํ–‰ ์‹œ ์Šคํ„ฐ๋น™์„ ํ•˜์ง€ ์•Š์œผ๋ฉด ๊ธฐ์กด ๊ฐ์ฒด์˜ ๋กœ์ง์„ ์‹คํ–‰ํ•œ ๊ฐ’์„, ์Šคํ„ฐ๋น™์„ ํ•œ ๊ฒฝ์šฐ์—” ์Šคํ„ฐ๋น™ ๊ฐ’์„ ๋ฆฌํ„ดํ•œ๋‹ค.

 

 

@InjectMock

@InjectMock์€ DI๋ฅผ @Mock์ด๋‚˜ @Spy๋กœ ์ƒ์„ฑ๋œ mock ๊ฐ์ฒด๋ฅผ ์ž๋™์œผ๋กœ ์ฃผ์ž…ํ•ด์ฃผ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด๋‹ค.

 

 

OngoingStubbing ๋ฉ”์†Œ๋“œ

when์— ๋„ฃ์€ ๋ฉ”์†Œ๋“œ์˜ ๋ฆฌํ„ด ๊ฐ’์„ ์ •์˜ํ•ด์ฃผ๋Š” ๋ฉ”์†Œ๋“œ์ด๋‹ค.

 

when({์Šคํ„ฐ๋น™ํ•  ๋ฉ”์†Œ๋“œ}).{OngoingStubbing ๋ฉ”์†Œ๋“œ};

 

 

๋ฉ”์†Œ๋“œ๋ช… ์„ค๋ช…
thenReturn ์Šคํ„ฐ๋น™ํ•œ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ํ›„ ์–ด๋–ค ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•  ๊ฑด์ง€ ์ •์˜
thenThrow ์Šคํ„ฐ๋น™ํ•œ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ํ›„ ์–ด๋–ค Exception์„ Throwํ•  ๊ฑด์ง€ ์ •์˜
thenCallRealMethod ์‹ค์ œ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ

 

 

Stubber ๋ฉ”์†Œ๋“œ

Stubber ๋ฉ”์†Œ๋“œ๋Š” OngoingStubbing๊ณผ ๋‹ค๋ฅด๊ฒŒ when์— ์Šคํ„ฐ๋น™ํ•  ํด๋ž˜์Šค๋ฅผ ๋„ฃ๊ณ  ๊ทธ ํ›„์— ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ๊ทธ ์ด์œ ๋Š” ์Šคํ„ฐ๋น™์ด ๋ฐ˜๋“œ์‹œ ์‹คํ–‰๋˜์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์†Œ๋“œ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

{Stubber ๋ฉ”์†Œ๋“œ}.when({์Šคํ„ฐ๋น™ํ•  ํด๋ž˜์Šค}).{์Šคํ„ฐ๋น™ํ•  ๋ฉ”์†Œ๋“œ}

 

 

๋ฉ”์†Œ๋“œ๋ช… ์„ค๋ช…
doReturn ์Šคํ„ฐ๋น™ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ํ›„ ์–ด๋–ค ํ–‰๋™์„ ํ•  ๊ฑด์ง€ ์ •์˜
doThrow ์Šคํ„ฐ๋น™ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ํ›„ ์–ด๋–ค Exception์„ throwํ•   ๊ฑด์ง€ ์ •์˜
doAnswer ์Šคํ„ฐ๋น™ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ํ›„ ์ž‘์—…์„ ํ• ์ง€ customํ•˜๊ฒŒ ์ •์˜
doNothing ์Šคํ„ฐ๋น™ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ํ›„ ์–ด๋–ค ํ–‰๋™๋„ ํ•˜์ง€ ์•Š๊ฒŒ ์ •์˜
doCallRealMethod ์‹ค์ œ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ

 

 

Verify

verify ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ์Šคํ„ฐ๋น™ํ•œ ๋ฉ”์†Œ๋“œ๊ฐ€ ์‹คํ–‰๋๋Š”์ง€, n๋ฒˆ ์‹คํ–‰๋๋Š”์ง€, ์‹คํ–‰์ด ์ดˆ๊ณผ๋˜์ง€ ์•Š์•˜๋Š”์ง€ ๋“ฑ ๋‹ค์–‘ํ•˜๊ฒŒ ๊ฒ€์ฆํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค. 

 

verify(T mock, VerificationMode mode).{๋ฉ”์†Œ๋“œ}

 

 

๋ฉ”์†Œ๋“œ๋ช… ์„ค๋ช… (ํ…Œ์ŠคํŠธ ๋‚ด์—์„œ~)
times(n) ๋ช‡ ๋ฒˆ์ด ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ
never ํ•œ ๋ฒˆ๋„ ํ˜ธ์ถœ๋˜์ง€ ์•Š์•˜๋Š”์ง€ ๊ฒ€์ฆ
atLeastOne ์ตœ์†Œ ํ•œ ๋ฒˆ์€ ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ
atLeast(n) ์ตœ์†Œ n ๋ฒˆ์ด ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ
atMostOnce ์ตœ๋Œ€ ํ•œ ๋ฒˆ์ด ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ
atMost(n) ์ตœ๋Œ€ n ๋ฒˆ์ด ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ
calls(n) n๋ฒˆ์ด ํ˜ธ์ถœ๋๋Š”์ง€ ๊ฒ€์ฆ (InOrder๋ž‘ ๊ฐ™์ด ์‚ฌ์šฉํ•ด์•ผ ํ•จ)
only ํ•ด๋‹น ๊ฒ€์ฆ ๋ฉ”์†Œ๋“œ๋งŒ ์‹คํ–‰๋๋Š”์ง€ ๊ฒ€์ฆ
timeout(long mills) n ms ์ด์ƒ ๊ฑธ๋ฆฌ๋ฉด Fail ๊ทธ๋ฆฌ๊ณ  ๋ฐ”๋กœ ๊ฒ€์ฆ ์ข…๋ฃŒ
after(long mills) n ms ์ด์ƒ ๊ฑธ๋ฆฌ๋Š”์ง€ ํ™•์ธ

timeout๊ณผ ๋‹ค๋ฅด๊ฒŒ ์‹œ๊ฐ„์ด ์ง€๋‚˜๋„ ๋ฐ”๋กœ ๊ฒ€์ฆ ์ข…๋ฃŒ๊ฐ€ ๋˜์ง€ ์•Š๋Š”๋‹ค.
description ์‹คํŒจํ•œ ๊ฒฝ์šฐ ๋‚˜์˜ฌ ๋ฌธ๊ตฌ

 

 

 

 

์˜ˆ์™ธ ํ…Œ์ŠคํŠธ

์˜ˆ์™ธ ๋ฐœ์ƒ์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ์—ˆ๋‹ค. ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” 4๊ฐ€์ง€๊ฐ€ ์žˆ์—ˆ๋Š”๋ฐ ๊ทธ ์ค‘ ๊ฐ€์žฅ ๊ฐ€๋…์„ฑ์ด ๋†’๊ณ  ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” assertj์˜ assertThatTrownBy๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค. ์˜ˆ์™ธ ํ…Œ์ŠคํŠธ 4๊ฐ€์ง€๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

 

1. assertThrows

2. assertj์˜ assertThatThrownBy

3. assertEquals (try ~ catch)

4. assertThrows ๋ฐ˜ํ™˜๊ฐ’ ์‚ฌ์šฉ

 

  • ์‚ฌ์šฉ ์˜ˆ์‹œ

 

assertThatThrownBy(() -> ticketingService.saveTicketing(memberId, request))
                .isInstanceOf(CustomException.class)
                .hasMessageContaining(ErrorCode.SEAT_INFO_NOT_VALID_FORMAT.getErrorMessage());

 

 

 

 

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ์—”ํ‹ฐํ‹ฐ @ID ๊ฐ’ Null ์ด์Šˆ

์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„  ์—”ํ‹ฐํ‹ฐ์— setter๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ๋กœ ํ–ˆ๋‹ค. ๋‹ค์–‘ํ•œ ์ด์œ ๋“ค์ด ์žˆ์ง€๋งŒ ํ•ด๋‹น ๊ฒŒ์‹œ๊ธ€์€ ํ…Œ์ŠคํŠธ ๊ด€๋ จ ๊ฒŒ์‹œ๊ธ€์ด๊ธฐ ๋•Œ๋ฌธ์— ๋„˜์–ด๊ฐ€๊ฒ ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ๋  ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ์ฝ”๋“œ์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ Id๊ฐ’์„ ์ง€์ •ํ•ด์ฃผ์ง€ ๋ชป ํ•ด nullPointException์ด ํ„ฐ์กŒ๋‹ค. 

Member member = memberRepository.findByEmail(email);
teamRepository.findTeamByMember(member.getId())

์œ„์™€ ๊ฐ™์€ ๋กœ์ง์˜ service๋ฅผ ํ…Œ์ŠคํŠธ ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž.

memberRepository์˜ ๋ฐ˜ํ™˜๊ฐ’์„ ๋”ฐ๋กœ ๋งŒ๋“  member๋กœ ์Šคํ„ฐ๋น™ ํ•ด์ค„ ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ member์—” Id๊ฐ’์ด null ์ด๋ฏ€๋กœ ์—๋Ÿฌ๊ฐ€ ๋‚˜๊ฒŒ ๋œ๋‹ค. 

 

teamRepository.findTeamByMember(member.getId())๋ฅผ ์Šคํ„ฐ๋น™ ํ•  ๋•Œ ์ธ์ž ๊ฐ’์œผ๋กœ null์„ ๋„˜๊ฒจ์ฃผ๋ฉด ํ•ด๊ฒฐ๋˜๊ธด ํ–ˆ์ง€๋งŒ ์ข‹์ง€ ์•Š์€ ๋ฐฉ๋ฒ• ๊ฐ™์•˜๋‹ค. 

 

๊ณ ๋ฏผ๋์— Mockito Spy๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค. 

Mockito์˜ spy๋Š” ๊ธฐ์กด ๊ฐ์ฒด๋ฅผ ๊ทธ๋Œ€๋กœ ๋“ค๊ณ  ์žˆ์œผ๋ฉด์„œ ํŠน์ • ๋ฉ”์„œ๋“œ๋งŒ ์Šคํ„ฐ๋น™ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ์ฒด์ด๋‹ค. 

์•„๋ž˜์™€ ๊ฐ™์ด member์˜ id๊ฐ’๋งŒ ์Šคํ„ฐ๋น™ํ•˜์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. 

 

Member spyMember = spy(member);
when(spyMember.getId()).thenReturn(1L);

 

 

 

 

Querydsl ์‚ฌ์šฉ ์‹œ DataJpaTest ์‹คํŒจ

ํ”„๋กœ์ ํŠธ์—์„œ querydsl ์„ ์„ค์ •์„ ํ•ด์ฃผ๊ณ  test case ์„ ์ž‘์„ฑํ•˜์˜€๋‹ค. ํ•˜์ง€๋งŒ jpaqueryfactory ๊ฐ€ bean ์ฃผ์ž…์„ ๋ฐ›์ง€ ๋ชปํ•˜๋ฉด์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ์ด ๋˜์—ˆ๋‹ค. ์—๋Ÿฌ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

 

No qualifying bean of type 'com.querydsl.jpa.impl.JPAQueryFactory' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

 

TestConfig ํด๋ž˜์Šค๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด ํ•ด๊ฒฐํ•ด์ฃผ์—ˆ๋‹ค. 

@TestConfiguration : test case ์ž‘์ •์‹œ ์„ค์ •์„ ์ฃผ์ž… ์‹œ์ผœ์ฃผ๋Š” ์–ด๋…ธํ…Œ์ด์…˜ 

@PersistenceContext : ์ด ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด์„œ EntityManager ์˜์†์„ฑ์„ ์ฃผ์ž…์‹œ์ผœ์คŒ.

 

@TestConfiguration
public class TestConfig {

    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory(){
        return new JPAQueryFactory(entityManager);
    }

}

 

 

๋งŒ๋“  TestConfig๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ @Import ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

 

 

 

 

 

Spring Security ์‚ฌ์šฉ ์‹œ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ์˜ ์ธ์ฆ, ์ธ๊ฐ€

์ปจํŠธ๋กค๋Ÿฌ ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด ํ†ตํ•ฉํ…Œ์ŠคํŠธ ์ž‘์„ฑ์ค‘์— ์‹œํ๋ฆฌํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ๊ฐ€ ์žˆ์–ด ์—๋Ÿฌ๊ฐ€ ํ„ฐ์กŒ๋‹ค. 

 

exception: java.lang.NullPointerException: Cannot invoke "com.tickerBell.global.security.context.MemberContext.getMember()" because "memberContext" is null

 

 

์ปจํŠธ๋กค๋Ÿฌ ์ฝ”๋“œ์˜ ์ผ๋ถ€์ด๋‹ค. 

 

 

์ปจํŠธ๋กค๋Ÿฌ ์ฝ”๋“œ์—์„œ @AuthenticationPrincipal ์„ ์‚ฌ์šฉํ•ด ํ˜„์žฌ ๋กœ๊ทธ์ธ ํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š”๋ฐ ์ด๋•Œ null ๊ฐ’์ด ๋“ค์–ด์˜ค๊ฒŒ ๋˜๊ณ  null ์— ์ ‘๊ทผํ•˜๋Š” ์ฝ”๋“œ์—์„œ NullPointerException์ด ํ„ฐ์ง„ ๊ฒƒ์ด๋‹ค. 

๊ทธ๋ ‡๋‹ค๊ณ  SpringSecurity๋ฅผ ํ†ตํ•ด์„œ ์ ‘๊ทผ์ œ์–ด๋ฅผ ํ•ด๋†“์€ URL๋กœ Controller Test๋ฅผ ํ•  ๋•Œ ๋งค๋ฒˆ ์ธ์ฆ๋กœ์ง(๋กœ๊ทธ์ธ, ํ† ํฐ..)์„ ๋ถ™์—ฌ์ฃผ๋Š”๊ฑด ํšจ์œจ์ด ๋งค์šฐ ๋–จ์–ด์กŒ๋‹ค. 

์ด๋•Œ @withUserDetailsํ™œ์šฉํ•˜๋ฉด Test์—์„œ header์— token์„ ๋„ฃ์–ด์ฃผ์ง€ ์•Š์•„๋„  ๊ฐ„ํŽธํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

@WithUserDetails

 

@withUserDetails๋Š” UserDetailsService๋ฅผ ํ†ตํ•ด์„œ User๊ฐ์ฒด๋ฅผ ์ง์ ‘ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‚˜์ฒ˜๋Ÿผ ๊ฐ์ฒด๊ฐ€ ์ง์ ‘ ํ•„์š”ํ•  ๋•Œ๋Š” ์ด ๋ฐฉ๋ฒ•์„ ์“ฐ๋Š”๊ฒŒ ์ข‹์€ ๊ฒƒ ๊ฐ™๋‹ค. 

 

value๋Š” ์–ด๋–ค username์„ ๊ฐ–์€ user๋ฅผ SecurityContextHolder์— ๋„ฃ์„์ง€ userDetailsServiceBeanName์€ ์–ด๋–ค UserDetailsService๋ฅผ ์ด์šฉํ• ์ง€ ์•Œ๋ ค์ฃผ๋Š” ๊ฒƒ์ด๋‹ค. ๋งŒ์•ฝ Custom์„ ํ–ˆ๋‹ค๋ฉด ๊ทธ๊ฒƒ์„ ์ ์–ด์ฃผ๋ฉด๋œ๋‹ค. ๋‚˜๋Š” ๋”ฐ๋กœ ์ปค์Šคํ…€ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ๋”ฐ๋กœ ์ ์–ด์ฃผ์ง€ ์•Š์•˜๋‹ค.

 

ํ•˜์ง€๋งŒ ์ด๋•Œ value๋Š” ์‹ค์ œ๋กœ ํ•ด๋‹น username์„ ๊ฐ€์ง„ user๊ฐ€ ์กด์žฌํ•ด์•ผํ•œ๋‹ค.

๋”ฐ๋ผ์„œ Test์ง„ํ–‰์ „์— user๊ฐ์ฒด๋ฅผ ๋„ฃ์–ด๋†”์•ผํ•˜๊ธฐ๋•Œ๋ฌธ์— @BeforeEach๋ฅผ ํ†ตํ•ด์„œ user๋ฅผ ์ €์žฅํ•ด๋†“์œผ๋ฉด ๋œ๋‹ค.

 

 

๋˜ ๋‹ค๋ฅธ ์ด์Šˆ! 

SecurityContext๋Š” default๋กœ TestExecutionListener.beforeTestMethod๋กœ ์„ค์ •์ด ๋˜์–ด์žˆ๋‹ค.

์ฆ‰ @Before ์ „์— @withUserDetails์ด ๋™์ž‘ํ•ด์„œ SecurityContext์•ˆ์— ๋„ฃ์œผ๋ ค๊ณ ํ•œ๋‹ค.

๋”ฐ๋ผ์„œ ์‹ค user๊ฐ์ฒด๊ฐ€ ์ƒ๊ธฐ๊ธฐ์ „์— ํ•ด๋‹น user๋ฅผ ์ฐพ๊ฒŒ ๋˜๊ณ  ์‹คํŒจํ•˜๊ฒŒ ๋œ๋‹ค.

ํ•ด๊ฒฐ๋ฒ•์œผ๋กœ ์•„๋ž˜ ์˜ต์…˜์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

 

setupBefore = TestExecutionEvent.TEST_EXECUTION)