๐Ÿ“Œย 8์ฃผ์ฐจ 11์žฅ ์˜์กด๊ด€๊ณ„ ์ฃผ์ž…(DI)๋ฅผ ํ†ตํ•œ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์‰ฌ์šด ์ฝ”๋“œ ๋งŒ๋“ค๊ธฐ

โœ…ย Controller, Service์—์„œ ์ƒ์„ฑํ•ด์„œ ์‚ฌ์šฉํ•˜๋˜ ์˜์กด๊ด€๊ณ„๋ฅผ ์ƒ์„ฑ์ž ์ฃผ์ž…์œผ๋กœ ๋ณ€๊ฒฝ

๊ธฐ์กด Service ์ฝ”๋“œ

๊ธฐ์กด QuestionService๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด dao ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค.

Untitled

์œ„ ํ˜•ํƒœ๋Š” dao๊ฐ์ฒด๊ฐ€ Service์— ๊ฐ•ํ•˜๊ฒŒ ๊ฒฐํ•ฉํ•œ ํ˜•ํƒœ์ด๋ฉฐ ๊ฐ•ํ•œ ๊ฒฐํ•ฉ์œผ๋กœ ์ธํ•ด DB๋ฅผ ์ œ์™ธํ•œ ์ˆœ์„œ ์„œ๋น„์Šค ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์‰ฝ์ง€ ์•Š๋‹ค.

DI ์ ์šฉ(์ƒ์„ฑ์ž ์ฃผ์ž…) Service

Untitled

QuestionService ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ• ๋•Œ ํ•„์š”ํ•œ dao์— ๋Œ€ํ•œ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•˜๋„๋ก ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

DI ์ ์šฉ Service ํ…Œ์ŠคํŠธ

@ExtendWith(MockitoExtension.class)
class QuestionServiceTest {

    @InjectMocks
    QuestionService questionService;

    @Mock
    QuestionDao questionDao;

    @Mock
    AnswerDao answerDao;

    @Test
    @DisplayName("์กด์žฌํ•˜์ง€ ์•Š๋Š” ์‚ฌ์šฉ์ž ์งˆ๋ฌธ ์‚ญ์ œ์‹œ ์˜ˆ์™ธ ๋ฐœ์ƒ ํ…Œ์ŠคํŠธ")
    void deleteNotExistQuestion_fail_withException() {
        // given
        given(answerDao.findAllByQuestionId(1L)).willReturn(Collections.emptyList());
        given(questionDao.findById(1L)).willReturn(null);

        // then
        Assertions.assertThatThrownBy(
                        () -> questionService.deleteQuestion(1L, new User("test", "1234", "name", "[email protected]")))
                .isInstanceOf(CannotDeleteQuestionException.class)
                .hasMessageContaining("์กด์žฌํ•˜์ง€ ์•Š๋Š” ์งˆ๋ฌธ๊ธ€์€ ์‚ญ์ œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
    }

    @Test
    @DisplayName("ํƒ€์ธ์ด ์ž‘์„ฑํ•œ ์งˆ๋ฌธ ์‚ญ์ œ์‹œ ์˜ˆ์™ธ ๋ฐœ์ƒ ํ…Œ์ŠคํŠธ")
    void deleteNotSameUserQuestion_fail_withException() {
        // given
        given(answerDao.findAllByQuestionId(1L)).willReturn(Collections.emptyList());
        given(questionDao.findById(1L)).willReturn(new Question(1L, "differentWriter", "test", "test", new Date(), 0));

        // then
        Assertions.assertThatThrownBy(
                        () -> questionService.deleteQuestion(1L, new User("test", "1234", "name", "[email protected]")))
                .isInstanceOf(CannotDeleteQuestionException.class)
                .hasMessageContaining("๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ๊ธ€์€ ์‚ญ์ œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
    }

    @Test
    @DisplayName("๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ์งˆ๋ฌธ์ด ๋‹ฌ๋ ค์žˆ๋Š” ์งˆ๋ฌธ ์‚ญ์ œ์‹œ ์˜ˆ์™ธ ๋ฐœ์ƒ ํ…Œ์ŠคํŠธ")
    void deleteQuestionWithOtherUserAnswer_fail_withException() {
        // given
        given(answerDao.findAllByQuestionId(1L)).willReturn(List.of(new Answer(1L, "differentWriter", "test", new Date(), 1L)));
        given(questionDao.findById(1L)).willReturn(new Question(1L, "test", "test", "test", new Date(), 0));

        // then
        Assertions.assertThatThrownBy(
                        () -> questionService.deleteQuestion(1L, new User("test", "1234", "name", "[email protected]")))
                .isInstanceOf(CannotDeleteQuestionException.class)
                .hasMessageContaining("์งˆ๋ฌธ์— ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ๋‹ต๋ณ€์ด ๋‹ฌ๋ ค์žˆ์œผ๋ฏ€๋กœ ์‚ญ์ œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
    }

    @Test
    @DisplayName("๋‹ต๋ณ€์ด ์—†๋Š” ์‚ฌ์šฉ์ž์˜ ์งˆ๋ฌธ ์‚ญ์ œ์‹œ ์ •์ƒ ๋™์ž‘ ํ…Œ์ŠคํŠธ")
    void deleteQuestionWithNoAnswer_success() {
        // given
        given(answerDao.findAllByQuestionId(1L)).willReturn(Collections.emptyList());
        given(questionDao.findById(1L)).willReturn(new Question(1L, "test", "test", "test", new Date(), 0));

        // then
        Assertions.assertThatNoException().isThrownBy(
                () -> questionService.deleteQuestion(1L, new User("test", "1234", "name", "[email protected]")));
    }

    @Test
    @DisplayName("์ž์‹ ์ด ๋‹ต๋ณ€์„ ๋‹จ ์งˆ๋ฌธ ์‚ญ์ œ์‹œ ์ •์ƒ ๋™์ž‘ ํ…Œ์ŠคํŠธ")
    void deleteQuestionWithAnswers_success() {
        // given
        given(answerDao.findAllByQuestionId(1L)).willReturn(List.of(new Answer(1L, "test", "test", new Date(), 1L)));
        given(questionDao.findById(1L)).willReturn(new Question(1L, "test", "test", "test", new Date(), 0));

        // then
        Assertions.assertThatNoException().isThrownBy(
                () -> questionService.deleteQuestion(1L, new User("test", "1234", "name", "[email protected]")));

    }

}

DI๋ฅผ ์ ์šฉํ•œ QuestionService์—์„œ Mockito๋ฅผ ์ด์šฉํ•˜์—ฌ ์‹ค์ œ DB์™€ ์—ฐ๊ฒฐํ•˜์ง€ ์•Š๊ณ , ์ˆœ์ˆ˜ ์„œ๋น„์Šค ์ฝ”๋“œ๋งŒ ๊ฒฉ๋ฆฌํ•ด ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋‹ค.

โ—๏ธ ์ด์Šˆ: QuestionServiceTest ์‹คํŒจ: Mocito์˜ UnnecessaryStubbingException

์ปจํŠธ๋กค๋Ÿฌ, ๋งคํ•‘ ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง

๊ธฐ์กด ๋ ˆ๊ฑฐ์‹œ ์ปจํŠธ๋กค๋Ÿฌ, New MVC ์ปจํŠธ๋กค๋Ÿฌ ๋ฐ Legacy Handler Mapping ์ฝ”๋“œ๋“ค์€ ์ƒ์„ฑ์ž ์˜์กด์„ฑ ์ฃผ์ž…์ด ์ ์šฉ๋˜์ง€ ์•Š์€ ์ƒํƒœ์ด๋ฏ€๋กœ ์ด ๋˜ํ•œ ์ƒ์„ฑ์ž๋กœ ์ฃผ์ž…๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ์ˆ˜์ •ํ•œ๋‹ค.