Backend Language/TDD

[์ฐธ๊ณ ] Mockito ๊ธฐ์ดˆ ์‚ฌ์šฉ๋ฒ•

chaerlo127 2023. 2. 12. 18:34
728x90

๐ŸŒบ Mockito๋ž€

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

 

 

๐ŸŒบ dependencies ์ถ”๊ฐ€ (Gradle)

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'

	// junit
	testImplementation('org.junit.jupiter:junit-jupiter:5.5.0')

	// mockito -> ์ด๋ถ€๋ถ„ ์ถ”๊ฐ€
	testImplementation('org.mockito:mockito-core:4.5.1')
}

 

๐ŸŒบ ๋ชจ์˜ ๊ฐ์ฒด ์ƒ์„ฑ

@Test
    void mockTest(){
        GameNumGen getMock = mock(GameNumGen.class); // ๋ชจ์˜ ๊ฐ์ฒด ์ƒ์„ฑ ์˜ˆ
    }
}

 

  • Mocktio.mock(): ํด๋ž˜์Šค, ์ธํ„ฐํŽ˜์ด์Šค, ์ถ”์ƒ ํด๋ž˜์Šค์— ๋Œ€ํ•ด ๋ชจ์˜ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Œ'

 

๐ŸŒบ ์Šคํ… ์„ค์ •

๋ชจ์˜ ๊ฐ์ฒด๋ฅผ ์„ค์ •ํ•œ ์ดํ›„์—๋Š” BDDMockito ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ชจ์˜ ๊ฐ์ฒด์— ์Šคํ…์„ ๊ตฌ์„ฑํ•ญํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 @Test
    void mockTest(){
        GameNumGen genMock = mock(GameNumGen.class); // ๋ชจ์˜ ๊ฐ์ฒด ์ƒ์„ฑ ์˜ˆ
        given(genMock.generate(GameLevel.EASY)).willReturn("123"); // given: ๋ชจ์˜ ๊ฐ์ฒด ๋งค์„œ๋“œ ํ˜ธ์ถœ์„ ์ „๋‹ฌ

        String num = genMock.generate(GameLevel.EASY);
        assertEquals("123", num);
    }


@Test
    void mockThrowTest(){
        GameNumGen genMock = mock(GameNumGen.class);
        given(genMock.generate(null)).willThrow(IllegalArgumentException.class);
//        given(genMock.generate(null)).willThrow(new IllegalArgumentException()); ์ด๊ฒƒ๋„ ๊ฐ€๋Šฅ

        assertThrows(
                IllegalArgumentException.class,
                () -> genMock.generate(null)
        );
    }

 

  • given(): ์Šคํ…์„ ์ •์˜ํ•  ๋ชจ์˜ ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์„ ์ „๋‹ฌ
  • willReturn(): ์Šคํ…์„ ์ •์˜ํ•œ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฆฌํ„ดํ•  ๊ฐ’์„ ์ง€์ •
  • willThrow(): ์Šคํ…์„ ์ •์˜ํ•œ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฆฌํ„ดํ•  ์˜ˆ์™ธ์ฒ˜๋ฆฌ ์ง€์ •

์ฆ‰, ์œ„์˜ ์ฝ”๋“œ์—์„œ generate๋ฅผ ์‹คํ–‰ํ•˜๋ฉด given์—์„œ ์ง€์ •ํ•œ willReturn ๊ฐ’์œผ๋กœ 123์„ ๋„˜๊ฒจ์ค€๋‹ค. 

 

return ํƒ€์ž…์ด void์ธ method์˜ ๊ฒฝ์šฐ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด Test ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค. ๋ฉ”์†Œ๋“œ ๋’ค์— willThrow๊ฐ€ ๋ถ™๋Š” ๊ฒƒ์ด ์•„๋‹Œ, ์ต์…‰์…˜ ํƒ€์ž…, ๊ฐ์ฒด๋ฅผ ์ธ์ž๋กœ ๋ฐ›๊ณ  ๋‚œ ํ›„, given ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด์„œ ๋ชจ์˜ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌ๋ฐ›๋Š”๋‹ค.

๋‹จ, willThrow ๋’ค์— ๋ถ™๋Š” given ์˜ ๊ฒฝ์šฐ์—๋Š” ๋ชจ์˜ ๊ฐ์ฒด์˜ ๋ฉ”์†Œ๋“œ๊ฐ€ ์•„๋‹Œ ๋ชจ์˜ ๊ฐ์ฒด ๊ทธ ์ž์ฒด์ธ ๊ฒƒ์„ ์œ ์˜ํ•ด์•ผ ํ•œ๋‹ค.

 @Test
    void voidMethodWillThrowTest(){
        List<String> mockList = mock(List.class);
        willThrow(UnsupportedOperationException.class)
                .given(mockList)
                .clear();

        assertThrows(
                UnsupportedOperationException.class,
                () -> mockList.clear()
        );
    }

 

 

๐ŸŒผ ์ธ์ž ๋งค์นญ ์ฒ˜๋ฆฌ

given(genMock.generate(GameLevel.EASY)).willReturn("123"); 
String num = genMock.generate(GameLevel.NORMAL);

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๊ฐ€ ์ž‘์„ฑ์ด ๋˜์–ด ์žˆ์„ ๊ฒฝ์šฐ์—๋Š” EASY enum์—์„œ๋งŒ 123 return ๊ฐ’์ด ์žˆ์œผ๋ฏ€๋กœ NORMAL์„ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ํ•  ๋•Œ ๊ฐ’์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— null ์ด return ๋œ๋‹ค. 

 

์ด์™€ ๊ฐ™์ด, Mockito๋Š” ์Šคํ… ์„ค์ •๊ณผ ์ผ์น˜ํ•˜๋Š” ์ธ์ž๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ์—๋Š” ๋ฆฌํ„ด ํƒ€์ž…์˜ ๊ธฐ๋ณธ ๊ฐ’์„ return ํ•œ๋‹ค. int์˜ ๊ฒฝ์šฐ์—๋Š” 0, boolean์˜ ๊ฒฝ์šฐ์—๋Š” false์™€ ๊ฐ™์ด ๋ง์ด๋‹ค. ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ์—†์„ ๊ฒฝ์šฐ์—๋Š” null์„ return ํ•œ๋‹ค. 

 

 

@Test
    void anyMatchTest(){
        GameNumGen genMock = mock(GameNumGen.class);
        given(genMock.generate(any())).willReturn("456");

        String num = genMock.generate(GameLevel.EASY);
        assertEquals("456", num);

        String num2 = genMock.generate(GameLevel.NORMAL);
        assertEquals("456", num2);
    }
  • any(): ๋ชจ๋“  ๊ฐ’์— ์ผ์น˜ํ•˜๋„๋ก ์Šคํ… ์„ค์ •, ์ž„์˜์˜ ๊ฐ’์— ๋Œ€ํ•œ ์ผ์น˜
  • anyInt(), anyShort(), anyLong(), anyByte, anyChar(), anyDouble() ๋“ฑ๋“ฑ
  • anyString()
  • anyList(), anySet(), anyMap(), anyCollection(): ์ž„์˜์˜ ์ฝœ๋ž™์…˜์— ๋Œ€ํ•œ ์ผ์น˜
  • matches(String), matches(Pattern): ์ •๊ทœ ํ‘œํ˜„ ์‹์„ ์ด์šฉํ•œ String ๊ฐ’ ์ผ์น˜ ์—ฌ๋ถ€
  • eq(๊ฐ’): ํŠน์ • ๊ฐ’๊ณผ ์ผ์น˜ ์—ฌ๋ถ€

 

๋˜ํ•œ, ์ž„์˜์˜ ๊ฐ’๊ณผ ์ผ์น˜ํ•˜๋Š” ์ธ์ž์™€ ์ •ํ™•ํ•˜๊ฒŒ ์ผ์น˜ํ•˜๋Š” ์ธ์ž๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด eq() ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค. 

given(mockList.set(anyInt(), "123")).willReturn("456"); // ์—๋Ÿฌ
given(mockList.set(anyInt(), eq("123"))).willReturn("456"); // ํ•ด๊ฒฐ

 

๐ŸŒบ ํ–‰์œ„ ๊ฒ€์ฆ

๋ชจ์˜ ๊ฐ์ฒด์˜ ์—ญํ•  ์ค‘ ํ•˜๋‚˜๋Š” ์‹ค์ œ ๋ชจ์˜ ๊ฐ์ฒด๊ฐ€ ๋ถˆ๋ ธ๋Š”์ง€ ๊ฒ€์ฆํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด genMock์˜ ๋ชจ์˜ ๊ฐ์ฒด์˜ generate ๋ฉ”์†Œ๋“œ๊ฐ€ EASY ์ธ์ž๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์‹ค์ œ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€ ๊ฒ€์ฆ์„ ํ•œ๋‹ค.

@Test
    void init(){
        GameNumGen genMock = mock(GameNumGen.class);
        Game game = new Game(genMock);
        game.init(GameLevel.EASY);

        then(genMock).should().generate(GameLevel.EASY); // then : ๋ชจ์˜ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌ ๋ฐ›์Œ should : ๋ชจ์˜ ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ถˆ๋ ค์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์„ค์ •
    }

 

์ •ํ™•ํ•œ ๊ฐ’์ด ์•„๋‹ˆ๋ผ, ํ˜ธ์ถœ ๋˜์—ˆ๋Š”์ง€๋งŒ ํ™•์ธํ•˜๊ณ  ์‹ถ์œผ๋ฉด any() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. 

๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ํšŸ์ˆ˜๋ฅผ ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•ด Mockito ํด๋ž˜์Šค๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. 

  • only() : ํ•œ๋ฒˆ๋งŒ ํ˜ธ์ถœ
  • times(int) : ์ง€์ •ํ•œ ํšŸ์ˆ˜๋งŒํผ ํ˜ธ์ถœ
  • never() : ํ˜ธ์ถœํ•˜์ง€ ์•Š์Œ
  • atLeast(int) ; ์ ์–ด๋„ ์ง€์ •ํ•œ ํšŸ์ˆ˜๋งŒํผ ํ˜ธ์ถœ
  • atLeastOnce() : atLeast(1)
  • atMost(int) : ์ตœ๋Œ€ ์ง€์ •ํ•œ ํšŸ์ˆ˜๋งŒํผ ํ˜ธ์ถœ

 

๐ŸŒบ ์ธ์ž ์บก์ณ

 ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋‹ค๋ณด๋ฉด, ๋ชจ์˜ ๊ฐ์ฒด๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์‚ฌ์šฉํ•œ ์ธ์ž๋ฅผ ๊ฒ€์ฆํ•ด์•ผํ•  ๋•Œ๊ฐ€ ์žˆ๋‹ค. String, int์˜ ๊ฒฝ์šฐ๋Š” ํ™•์ธํ•˜๊ธฐ ์‰ฝ์ง€๋งŒ ๋งŽ์€ ์†์„ฑ์„ ๊ฐ–๊ณ  ์žˆ๋Š” ๊ฐ์ฒด์˜ ๊ฒฝ์šฐ ์‰ฝ๊ฒŒ ๊ฒ€์ฆํ•˜๊ธฐ๋Š” ์‰ฝ์ง€ ์•Š๋‹ค. ์ด ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์ธ์ž ์บก์ณ์ด๋‹ค. 

 

์ธ์ž ์บก์ณ์™€ ๊ด€๋ จ๋œ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. 

 @DisplayName("๊ฐ€์ž…ํ•˜๋ฉด ๋ฉ”์ผ์„ ์ „์†กํ•จ")
    @Test
    void whenRegisterThenSendMail(){
        userRegister.register("id", "pw", "email");

        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class); // String ํƒ€์ž…์˜ ์ธ์ž ๋ณด๊ด€
        then(mockEmailNotifier)
                .should().sendRegisterEmail(captor.capture());

        String realEmail = captor.getValue();
        assertEquals("email", realEmail);
    }

 

728x90